/*
 * @(#)WorkflowOperationManager.java
 *
 * Copyright (c) 2003 DCIVision Ltd
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of DCIVision
 * Ltd ("Confidential Information").  You shall not disclose such Confidential
 * Information and shall use it only in accordance with the terms of the license
 * agreement you entered into with DCIVision Ltd.
 */
package com.dcivision.workflow.core;

// for schedule job
import java.io.File;
import java.sql.Connection;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SimpleTrigger;

import com.dcivision.alert.core.AdapterMaster;
import com.dcivision.form.bean.FormSubmission;
import com.dcivision.form.dao.FormSubmissionDAObject;
import com.dcivision.framework.AdapterMasterFactory;
import com.dcivision.framework.ApplicationException;
import com.dcivision.framework.ApplicationExceptionList;
import com.dcivision.framework.ApplicationMessageList;
import com.dcivision.framework.Crypt;
import com.dcivision.framework.ErrorConstant;
import com.dcivision.framework.GlobalConstant;
import com.dcivision.framework.MessageResourcesFactory;
import com.dcivision.framework.RecordNotFoundException;
import com.dcivision.framework.SchedulerFactory;
import com.dcivision.framework.SessionContainer;
import com.dcivision.framework.SystemParameterConstant;
import com.dcivision.framework.SystemParameterFactory;
import com.dcivision.framework.TextUtility;
import com.dcivision.framework.Utility;
import com.dcivision.framework.bean.ProtectedObject;
import com.dcivision.framework.bean.ProtectedResource;
import com.dcivision.framework.dao.ProtectedObjectDAObject;
import com.dcivision.framework.dao.ProtectedResourceDAObject;
import com.dcivision.user.bean.UserDelegation;
import com.dcivision.user.dao.MtmUserRecordUserGroupDAObject;
import com.dcivision.user.dao.MtmUserRecordUserRoleDAObject;
import com.dcivision.user.dao.UserDelegationDAObject;
import com.dcivision.workflow.WorkflowErrorConstant;
import com.dcivision.workflow.bean.MtmWfStepFormRecord;
import com.dcivision.workflow.bean.MtmWfStepNotificationRule;
import com.dcivision.workflow.bean.MtmWfStepNotificationTarget;
import com.dcivision.workflow.bean.MtmWorkflowInterruptUserActor;
import com.dcivision.workflow.bean.MtmWorkflowProgressSystemObject;
import com.dcivision.workflow.bean.MtmWorkflowProgressUserRecord;
import com.dcivision.workflow.bean.MtmWorkflowRecordObjectLink;
import com.dcivision.workflow.bean.MtmWorkflowStepPriorityRule;
import com.dcivision.workflow.bean.MtmWorkflowStepUserActor;
import com.dcivision.workflow.bean.MtmWorkflowStepWorkflowStep;
import com.dcivision.workflow.bean.WorkflowAction;
import com.dcivision.workflow.bean.WorkflowProgress;
import com.dcivision.workflow.bean.WorkflowProgressAttachment;
import com.dcivision.workflow.bean.WorkflowRecord;
import com.dcivision.workflow.bean.WorkflowStep;
import com.dcivision.workflow.bean.WorkflowStepFormEquat;
import com.dcivision.workflow.bean.WorkflowStepParameterValue;
import com.dcivision.workflow.bean.WorkflowStepRoute;
import com.dcivision.workflow.dao.MtmWfStepFormRecordDAObject;
import com.dcivision.workflow.dao.MtmWfStepNotificationRuleDAObject;
import com.dcivision.workflow.dao.MtmWfStepNotificationTargetDAObject;
import com.dcivision.workflow.dao.MtmWorkflowInterruptUserActorDAObject;
import com.dcivision.workflow.dao.MtmWorkflowProgressSystemObjectDAObject;
import com.dcivision.workflow.dao.MtmWorkflowProgressUserRecordDAObject;
import com.dcivision.workflow.dao.MtmWorkflowRecordObjectLinkDAObject;
import com.dcivision.workflow.dao.MtmWorkflowStepPriorityRuleDAObject;
import com.dcivision.workflow.dao.MtmWorkflowStepUserActorDAObject;
import com.dcivision.workflow.dao.MtmWorkflowStepWorkflowStepDAObject;
import com.dcivision.workflow.dao.WorkflowActionDAObject;
import com.dcivision.workflow.dao.WorkflowProgressAttachmentDAObject;
import com.dcivision.workflow.dao.WorkflowProgressDAObject;
import com.dcivision.workflow.dao.WorkflowRecordDAObject;
import com.dcivision.workflow.dao.WorkflowStageDAObject;
import com.dcivision.workflow.dao.WorkflowStepDAObject;
import com.dcivision.workflow.dao.WorkflowStepFormEquatDAObject;
import com.dcivision.workflow.dao.WorkflowStepParameterValueDAObject;
import com.dcivision.workflow.dao.WorkflowStepRouteDAObject;
import com.dcivision.workflow.layout.PositionTreeNode;
import com.dcivision.workflow.layout.WorkflowTree;



/**
 * WorkflowOperationManager.java
 *
 * This class is responsible for workflow operation.
 *
 * @author Rollo Chan
 * @company DCIVision Limited
 * @creation date 17/07/2003
 * @version $Revision: 1.220.2.7 $
 */

public class WorkflowOperationManager {
  
  public static final String REVISION = "$Revision: 1.220.2.7 $";
  
  protected Log log = LogFactory.getLog(this.getClass().getName());
  private SessionContainer sessionContainer = null;
  private Connection dbConn = null;
  private AdapterMaster am = null;
  
  private Vector scheduledJobVec = new Vector();
  
  /**
   * Constructor
   *
   * @param sessionContainer
   * @param conn The database connection
   */
  public WorkflowOperationManager(SessionContainer sessionContainer, Connection conn) {
    this.sessionContainer = sessionContainer;
    this.dbConn = conn;
    am = AdapterMasterFactory.getAdapterMaster(sessionContainer, conn);
  }
  /**
   * Gets the whole list of scheduled jobs. The schedule jobs are stored as a list of
   * Vector. Each schedule job itself is a Vector containing the useful objects,
   * WorkflowStep, WorkflowProgress, etc.
   * 
   * @return Vector of the schedule jobs
   */
  public Vector getScheduledJobVector() {
    log.debug("job vector size: "+ scheduledJobVec.size());
    return this.scheduledJobVec;
  }
  /**
   * Clears the temporarily scheduled jobs
   */
  public void clearScheduledJobVector() {
    this.scheduledJobVec.clear();
  }
  
  /**
   * Creates the workflow record and create the "Start" and "End" steps automatically.
   *
   * @param workflowRecord The workflow record to be created
   * @return The created workflow record object, with ID
   * @throws ApplicationException
   * @see com.dcivision.workflow.bean.WorkflowRecord
   * @see com.dcivision.workflow.bean.WorkflowStep
   * @see com.dcivision.workflow.bean.MtmWorkflowStepWorkflowStep
   */
  public WorkflowRecord createWorkflowRecord(WorkflowRecord workflowRecord) throws ApplicationException {
    WorkflowRecord result = null;
    
    // Create the master record.
    WorkflowRecordDAObject workflowRecordDAO = new WorkflowRecordDAObject(this.sessionContainer, this.dbConn);
    result = (WorkflowRecord)workflowRecordDAO.insertObject(workflowRecord);
    
    // Create start and end steps.
    WorkflowStepDAObject workflowStepDAO = new WorkflowStepDAObject(this.sessionContainer, this.dbConn);
    WorkflowStep startWorkflowStep = new WorkflowStep();
    startWorkflowStep.setWorkflowRecordID(result.getID());
    startWorkflowStep.setStepSeq(WorkflowStep.START_STEP_SEQ_NO);
    startWorkflowStep.setStepName(MessageResourcesFactory.getMessage(sessionContainer.getSessionLocale(), "workflow.label.default_start_step_name"));
    startWorkflowStep.setActionType(WorkflowStep.ACTION_TYPE_SUBMIT);
    startWorkflowStep.setWorkflowRule("1");
    startWorkflowStep.setPriority(GlobalConstant.PRIORITY_LOW);
    startWorkflowStep = (WorkflowStep)workflowStepDAO.insertObject(startWorkflowStep);
    WorkflowStep endWorkflowStep = new WorkflowStep();
    endWorkflowStep.setWorkflowRecordID(result.getID());
    endWorkflowStep.setStepSeq(WorkflowStep.END_STEP_SEQ_NO);
    endWorkflowStep.setStepName(MessageResourcesFactory.getMessage(sessionContainer.getSessionLocale(), "workflow.label.default_end_step_name"));
    endWorkflowStep.setActionType(WorkflowStep.ACTION_TYPE_END);
    endWorkflowStep.setWorkflowRule("1");
    endWorkflowStep.setPriority(GlobalConstant.PRIORITY_LOW);
    endWorkflowStep = (WorkflowStep)workflowStepDAO.insertObject(endWorkflowStep);
    
    // Create the linkage of start and end steps.
    MtmWorkflowStepWorkflowStepDAObject mtmWorkflowStepDAObject = new MtmWorkflowStepWorkflowStepDAObject(this.sessionContainer, this.dbConn);
    MtmWorkflowStepWorkflowStep mtmWorkflowStep = new MtmWorkflowStepWorkflowStep();
    mtmWorkflowStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE);
    mtmWorkflowStep.setParentStepID(startWorkflowStep.getID());
    mtmWorkflowStep.setChildStepID(endWorkflowStep.getID());
    mtmWorkflowStep.setGroupID(new Integer(0));
    mtmWorkflowStep.setLoopBack("N");
    mtmWorkflowStep = (MtmWorkflowStepWorkflowStep)mtmWorkflowStepDAObject.insertObject(mtmWorkflowStep);
    
    return(result);
  }
  /**
   * Creates the workflow record and create the "Start" and "End" steps automatically.
   *
   * @param workflowRecord The workflow record to be copied. The version of the cloned
   *                       WorkflowRecord will be opted and set it explicitly before creation
   * @param bCopyAllSteps Copy all steps if true, otherwise only copy the WorkflowRecord
   *                      and a Start/End step
   * @return The cloned workflow record object
   * @throws ApplicationException
   * @see com.dcivision.workflow.bean.WorkflowRecord
   * @see com.dcivision.workflow.bean.MtmWorkflowInterruptUserActor
   */
  public WorkflowRecord copyWorkflowRecord(WorkflowRecord workflowRecord, boolean bCopyAllSteps) throws ApplicationException {
    WorkflowRecord result = null;
    Integer sourceRecordID = workflowRecord.getID();
    
    log.debug("source workflow record id: "+sourceRecordID);
    
    // Create the master record.
    WorkflowRecordDAObject workflowRecordDAO = new WorkflowRecordDAObject(this.sessionContainer, this.dbConn);
    MtmWorkflowRecordObjectLinkDAObject recordObjectLinkDAO = new MtmWorkflowRecordObjectLinkDAObject(this.sessionContainer, this.dbConn);
    MtmWorkflowInterruptUserActorDAObject interruptUserActorDAO = new MtmWorkflowInterruptUserActorDAObject(this.sessionContainer, this.dbConn);
    workflowRecord.setID(null);
    result = (WorkflowRecord)workflowRecordDAO.insertObject(workflowRecord);
    List objectLinkList = recordObjectLinkDAO.getListByWorkflowRecordID(sourceRecordID);
    for (int i=0; i<objectLinkList.size(); i++) {
      MtmWorkflowRecordObjectLink objectLink = (MtmWorkflowRecordObjectLink) objectLinkList.get(i);
      objectLink.setWorkflowRecordID(result.getID());
      recordObjectLinkDAO.insertObject(objectLink);
    }
    //  copy interrupt owner
    if (GlobalConstant.TRUE.equals(workflowRecord.getAllowInterrupt())) {
      List mtmWfInterruptActorList = interruptUserActorDAO.getListByWorkflowRecordID(sourceRecordID);
      if (!Utility.isEmpty(mtmWfInterruptActorList)) {
        for (int i=0; i < mtmWfInterruptActorList.size(); i++) {
          MtmWorkflowInterruptUserActor interruptUserActor = (MtmWorkflowInterruptUserActor)mtmWfInterruptActorList.get(i);
          interruptUserActor.setWorkflowRecordID(result.getID());
          interruptUserActor.setID(null);
          interruptUserActorDAO.insertObject(interruptUserActor);
        }
      }
    }
    
    WorkflowStepDAObject workflowStepDAO = new WorkflowStepDAObject(this.sessionContainer, this.dbConn);
    MtmWorkflowStepWorkflowStepDAObject stepStepDAO = new MtmWorkflowStepWorkflowStepDAObject(this.sessionContainer, this.dbConn);
    MtmWorkflowStepUserActorDAObject stepActorDAO = new MtmWorkflowStepUserActorDAObject(this.sessionContainer, this.dbConn);
    WorkflowStepRouteDAObject wfStepRouteDAO = new WorkflowStepRouteDAObject(this.sessionContainer, this.dbConn);
    WorkflowStepFormEquatDAObject wfFormEquatDAO = new WorkflowStepFormEquatDAObject(this.sessionContainer, this.dbConn);
    //added by nancy
    WorkflowStepParameterValueDAObject wfParameterValueDAO=new WorkflowStepParameterValueDAObject(this.sessionContainer, this.dbConn);
    //end
    MtmWfStepNotificationRuleDAObject notifyRuleDAO = new MtmWfStepNotificationRuleDAObject(sessionContainer, dbConn);
    MtmWfStepNotificationTargetDAObject notifyTargetDAO = new MtmWfStepNotificationTargetDAObject(sessionContainer, dbConn);
    MtmWorkflowStepPriorityRuleDAObject priorityRuleDAO = new MtmWorkflowStepPriorityRuleDAObject(this.sessionContainer, this.dbConn);
    if (!bCopyAllSteps) {
      // Create start and end steps.
      WorkflowStep startWorkflowStep = new WorkflowStep();
      startWorkflowStep.setWorkflowRecordID(result.getID());
      startWorkflowStep.setStepSeq(WorkflowStep.START_STEP_SEQ_NO);
      startWorkflowStep.setStepName(MessageResourcesFactory.getMessage(sessionContainer.getSessionLocale(), "workflow.label.default_start_step_name"));
      startWorkflowStep.setActionType(WorkflowStep.ACTION_TYPE_SUBMIT);
      startWorkflowStep.setWorkflowRule("1");
      startWorkflowStep.setPriority(GlobalConstant.PRIORITY_LOW);
      startWorkflowStep = (WorkflowStep)workflowStepDAO.insertObject(startWorkflowStep);
      WorkflowStep endWorkflowStep = new WorkflowStep();
      endWorkflowStep.setWorkflowRecordID(result.getID());
      endWorkflowStep.setStepSeq(WorkflowStep.END_STEP_SEQ_NO);
      endWorkflowStep.setStepName(MessageResourcesFactory.getMessage(sessionContainer.getSessionLocale(), "workflow.label.default_end_step_name"));
      endWorkflowStep.setActionType(WorkflowStep.ACTION_TYPE_END);
      endWorkflowStep.setWorkflowRule("1");
      endWorkflowStep.setPriority(GlobalConstant.PRIORITY_LOW);
      endWorkflowStep = (WorkflowStep)workflowStepDAO.insertObject(endWorkflowStep);
      
      // Create the linkage of start and end steps.
      MtmWorkflowStepWorkflowStepDAObject mtmWorkflowStepDAObject = new MtmWorkflowStepWorkflowStepDAObject(this.sessionContainer, this.dbConn);
      MtmWorkflowStepWorkflowStep mtmWorkflowStep = new MtmWorkflowStepWorkflowStep();
      mtmWorkflowStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE);
      mtmWorkflowStep.setParentStepID(startWorkflowStep.getID());
      mtmWorkflowStep.setChildStepID(endWorkflowStep.getID());
      mtmWorkflowStep.setGroupID(new Integer(0));
      mtmWorkflowStep.setLoopBack("N");
      mtmWorkflowStep = (MtmWorkflowStepWorkflowStep)mtmWorkflowStepDAObject.insertObject(mtmWorkflowStep);
    } else {
      List workflowStepList = workflowStepDAO.getListByWorkflowRecordID(sourceRecordID);
      Hashtable stepHash = new Hashtable();
      
      //* Copy the steps
      for (int i=0; i<workflowStepList.size(); i++) {
        WorkflowStep workflowStep = (WorkflowStep) workflowStepList.get(i);
        workflowStep.setWorkflowRecordID(result.getID());
        Integer oldStepID = workflowStep.getID();
        WorkflowStep newStep = (WorkflowStep) workflowStepDAO.insertObject(workflowStep);
        stepHash.put(oldStepID, newStep.getID());
        
        //* Copy the Workflow_Step_Route & Workflow_Step_Form_Equat
        if(WorkflowStep.ACTION_TYPE_USER_DEFINED_TASK.equals(workflowStep.getActionType()) ){
          List wfStepRouteList = wfStepRouteDAO.getListByWorkflowStepID(workflowStep.getID());
          for (int k=0; k<wfStepRouteList.size(); k++){
            WorkflowStepRoute newRoute = (WorkflowStepRoute)wfStepRouteList.get(k);
            newRoute.setWorkflowStepID(newStep.getID());
            wfStepRouteDAO.insertObject(newRoute);
          }
        } else if(WorkflowStep.ACTION_TYPE_FORM_DRIVEN.equals(workflowStep.getActionType()) ){
          List formEquatList = wfFormEquatDAO.getListByWorkflowStepID(workflowStep.getID());
          for(int n=0; n<formEquatList.size(); n++){
            WorkflowStepFormEquat formEquat = (WorkflowStepFormEquat)formEquatList.get(n);
            formEquat.setWorkflowStepID(newStep.getID());
            wfFormEquatDAO.insertObject(formEquat);
          }
          //added by nancy
        }else if(WorkflowStep.ACTION_TYPE_SYSTEM_AUTO.equals(workflowStep.getActionType())){
          List parameterList = wfParameterValueDAO.getListByStepID(workflowStep.getID());
          log.debug("new stepID>>>>>>>>>>>>>>>>"+newStep.getID());
          for(int n=0; n<parameterList.size(); n++){
            WorkflowStepParameterValue parameterValue = (WorkflowStepParameterValue)parameterList.get(n);
            parameterValue.setWorkflowStepID(newStep.getID());
            wfParameterValueDAO.insertObject(parameterValue);
          }
        }
        //end
        
        // Copy the step's protected strategy if they are existed.
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        this.copyProtectedResourceInfoRelatedToStep(oldStepID, newStep.getID());
        
        // Copy the permissions of form's elements related to resource step
        // if they are existed.
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        this.copyFormPermissionInfoRelatedToStep(oldStepID, newStep.getID());
      }
      
      //* Copy step step relation
      List stepStepList = stepStepDAO.getListByWorkflowID(sourceRecordID);
      for (int i=0; i<stepStepList.size(); i++) {
        MtmWorkflowStepWorkflowStep stepStep = (MtmWorkflowStepWorkflowStep) stepStepList.get(i);
        Integer parentStepID=(Integer)stepHash.get(stepStep.getParentStepID());
        Integer childStepID=(Integer)stepHash.get(stepStep.getChildStepID());

        //Add by Samlin  for dirty data (delete step but not delete mtm_step_by_step
        if (parentStepID==null || childStepID ==null) {
          continue;
        }     
        
        stepStep.setParentStepID(parentStepID);
        stepStep.setChildStepID(childStepID);
        stepStep = (MtmWorkflowStepWorkflowStep) stepStepDAO.insertObject(stepStep);
      }
      
      List stepList = workflowStepDAO.getListByWorkflowRecordID(sourceRecordID);
      for (int h = 0; !Utility.isEmpty(stepList) && h < stepList.size(); h++) {
        //      * Copy the owners
        WorkflowStep currentStep = (WorkflowStep)stepList.get(h);
        List stepActorList = stepActorDAO.getListByWorkflowStepID(currentStep.getID());
        for (int j=0; j<stepActorList.size(); j++) {
          MtmWorkflowStepUserActor stepActor = (MtmWorkflowStepUserActor) stepActorList.get(j);
          stepActor.setWorkflowStepID((Integer)stepHash.get(currentStep.getID()));
          // if step owners is step n 's action taker or step n's action taker report to
          if (MtmWorkflowStepUserActor.ACTOR_TYPE_ACTION_TAKER.equals(stepActor.getActorType())
              || MtmWorkflowStepUserActor.ACTOR_TYPE_REPORT_TO_OF_ACTION_TAKER.equals(stepActor.getActorType())) {
            stepActor.setActorID((Integer)stepHash.get(stepActor.getActorID()));
          }
          stepActor = (MtmWorkflowStepUserActor) stepActorDAO.insertObject(stepActor);
        }
        // Copy the priority rules
        List priorityRuleList = priorityRuleDAO.getListByWorkflowStepID(currentStep.getID());
        for (int j = 0; j < priorityRuleList.size(); j++) {
          MtmWorkflowStepPriorityRule priorityRule = (MtmWorkflowStepPriorityRule)priorityRuleList.get(j);
          priorityRule.setWorkflowStepID((Integer)stepHash.get(currentStep.getID()));
          priorityRule = (MtmWorkflowStepPriorityRule)priorityRuleDAO.insertObject(priorityRule);
        }
        // copy the notification Rules
        List notificationRuleList = notifyRuleDAO.getListByWorkflowStepID(currentStep.getID());
        for (int j = 0; j < notificationRuleList.size(); j++) {
          MtmWfStepNotificationRule notificationRule = (MtmWfStepNotificationRule)notificationRuleList.get(j);
          Integer sourceNotificationRuleID = notificationRule.getID();
          notificationRule.setWorkflowStepID((Integer)stepHash.get(currentStep.getID()));
          notificationRule = (MtmWfStepNotificationRule)notifyRuleDAO.insertObject(notificationRule);
          List notificationTargetList = notifyTargetDAO.getListByNotifyRuleID(sourceNotificationRuleID);
          for (int k = 0; k < notificationTargetList.size(); k++) {
            MtmWfStepNotificationTarget notificationTarget = (MtmWfStepNotificationTarget)notificationTargetList.get(k);
            if (MtmWorkflowStepUserActor.ACTOR_TYPE_ACTION_TAKER.equals(notificationTarget.getTargetType())
                || MtmWorkflowStepUserActor.ACTOR_TYPE_REPORT_TO_OF_ACTION_TAKER.equals(notificationTarget.getTargetType())
                || MtmWfStepNotificationTarget.TARGET_TYPE_OWNER.equals(notificationTarget.getTargetType())) {
              notificationTarget.setTargetID((Integer)stepHash.get(notificationTarget.getTargetID()));
            }
            notificationTarget.setNotifyRuleID(notificationRule.getID());
            notificationTarget = (MtmWfStepNotificationTarget)notifyTargetDAO.insertObject(notificationTarget);
          }
        }
      }
    }
    
    return result;
  }
  
  /**
   * Copy the protected info from the source step to target step and record it
   * into DB.
   * 
   * @param sourceStepID source step's ID.
   * @param targetStepID target step's ID.
   * @return if we find out the protected info from source step and success to
   *         do the copy then return true, else return false.
   * @throws ApplicationException
   *           it means that it is difficult to complete this copy process.
   * @throws NullPointerException if the given paremeter
   *           <code>sourceStepID</code> or
   *           <code>targetStepID</code> is <code>null</code>.
   */
  boolean copyProtectedResourceInfoRelatedToStep(Integer sourceStepID,
      Integer targetStepID) throws ApplicationException {
    
    if (sourceStepID == null || targetStepID == null) {
      throw new NullPointerException("You must provide the source step's ID and target step's ID for copy operation!");
    }
    
    ProtectedResourceDAObject protectedResouceDAO = new ProtectedResourceDAObject(
        this.sessionContainer, this.dbConn);
    ProtectedObjectDAObject protectedObjectDAO = new ProtectedObjectDAObject(
        this.sessionContainer, this.dbConn);

    // TODO: Should we check whether the protected object is related to the step
    // through other tag, except object's id?
    // There should be a tag record the protected object's type if there are
    // multiply protected object recorded in the same DB table.
    List protectedInfoListRelatedSourceStep = protectedObjectDAO
        .getListByObjectValue(sourceStepID);

    // Skip function if there is not any protected definition for source step.
    if (Utility.isEmpty(protectedInfoListRelatedSourceStep)) {
      return false;
    }

    // Copy all of protected info related to source step for target step.
    ProtectedObject sourceProtectedObject;
    ProtectedResource sourceprotectedResource;
    ProtectedResource targetprotectedResource;
    for (int i = 0; i < protectedInfoListRelatedSourceStep.size(); i++) {
      sourceProtectedObject = (ProtectedObject) protectedInfoListRelatedSourceStep
          .get(i);
      sourceprotectedResource = (ProtectedResource) protectedResouceDAO
          .getObjectByID(sourceProtectedObject.getResourceID());

      targetprotectedResource = (ProtectedResource) protectedResouceDAO
          .insertObject(sourceprotectedResource);

      sourceProtectedObject.setObjectValue(targetStepID.toString());
      sourceProtectedObject.setResourceID(targetprotectedResource.getID());
      sourceProtectedObject.setProtection(Crypt.decrypt(sourceProtectedObject
          .getProtection(), SystemParameterFactory
          .getSystemParameter(SystemParameterConstant.CRYPTO_SALT)));
      protectedObjectDAO.insertObject(sourceProtectedObject);
    }
    return true;
  }
  
  /**
   * Copy the form's permission info related to source step to target step and
   * record it into DB.
   * 
   * @param sourceStepID source step's ID.
   * @param targetStepID target step's ID.
   * @return if we find out form's permission info related to source step and
   *         success to do the copy then return true, else return false.
   * @throws ApplicationException It means that it is difficult to complete this copy process.
   * @throws NullPointerException if the given paremeter <code>sourceStepID</code> or
   *           <code>targetStepID</code> is <code>null</code>.
   */
  boolean copyFormPermissionInfoRelatedToStep(Integer sourceStepID,
      Integer targetStepID) throws ApplicationException {

    if (sourceStepID == null || targetStepID == null) {
      throw new NullPointerException(
          "You must provide the source step's ID and target step's ID for copy operation!");
    }

    MtmWfStepFormRecordDAObject mtmWfStepFormRecordDAO = new MtmWfStepFormRecordDAObject(
        this.sessionContainer, this.dbConn);

    List formPermissionInfoListRelatedSourceStep = mtmWfStepFormRecordDAO
        .getListByWorkflowStepID(sourceStepID);

    // If there is not any form permission reference to source step, skip this
    // copy process.
    if (Utility.isEmpty(formPermissionInfoListRelatedSourceStep)) {
      return false;
    }

    // Copy the permission info from source step and record it into DB for
    // target step.
    MtmWfStepFormRecord sourcePermissionInfo;
    for (int i = 0; i < formPermissionInfoListRelatedSourceStep.size(); i++) {
      sourcePermissionInfo = (MtmWfStepFormRecord) formPermissionInfoListRelatedSourceStep
          .get(i);
      sourcePermissionInfo.setWorkflowStepID(targetStepID);

      mtmWfStepFormRecordDAO.insertObject(sourcePermissionInfo);
    }
    return true;
  }
  
  /**
   * Validates the workflow before release. A workflow record should pass all
   * the conditions which include,
   * exception message put into exceptionList,and warning message put into warningMessageList
   * <pre>
   *      Check for each step:
   *      1. All steps should be assigned with task owners except
   *         1.1. automatic task
   *         1.2. form driven task
   *         1.3. end step
   *         1.4. the first step of interrupt control workflow
   *      2. dynamic assign owner is not allowed after automatic task
   *      3. dynamic assign due date is not allowed after automatic task
   *      4. Previous Task Action Taker's Report To can not follow the automatic task
   *      5. All the leaf node is connected to an End step
   *      6. Current step's due date must later than previous step.//not exception but warning message TC.add
   *      7. The auto task(s) related to DMS needed some handwork before execute is not allowed after the automatic task.
   *         a. DmsMajorCheckIn
   *         b. DmsNewVersion
   *         c. DmsNoUpdateCheckIn
   * </pre>
   * @param workflowRecord The workflow record to be validated
   * @return A List of application exception (The reason of failure) if any
   * @see com.dcivision.workflow.bean.WorkflowRecord
   * @see com.dcivision.workflow.bean.WorkflowStep
   */
  public ApplicationExceptionList validateWorkflow(WorkflowRecord workflowRecord,ApplicationMessageList warningList) {
    /************************************************************************
     * TODO: Validate the workflow before release.
     ************************************************************************/
    ApplicationExceptionList exceptionList = new ApplicationExceptionList();
     
    Integer workflowRecordID = workflowRecord.getID();
    HashMap mtmStepMap = new HashMap();
    
    try{
      // rebuild the workflow tree, see any step is not real..
      WorkflowStepManager stepMgr = new WorkflowStepManager(sessionContainer, dbConn);
      WorkflowStepDAObject stepDAObject=new WorkflowStepDAObject(sessionContainer, dbConn);
      
      List stepList = stepMgr.getWorkflowStepWithActorList(workflowRecordID);
      List mapList = stepMgr.getWorkflowStepRelationshipList(workflowRecordID);
      String actorType=null;
      WorkflowTree tree = new WorkflowTree();
      WorkflowStep firstStep = null;
      List nodeList = null;
      
      // Check for each step:
      //  1. All steps should be assigned task owners except
      //      a. automatic task
      //      b. form driven task
      //      c. end step
      //      d. the first step of interrupt control workflow
      //  2. dynamic assign owner is not allowed after automatic task
      //  3. dynamic assign due date is not allowed after automatic task
      //  4. Previous Task Action Taker's Report To can not follow the automatic task
      //  5. All the leaf node is connected to an End step
      //  6. Current step's due date must later than previous step.
      //  7. The auto task(s) related to DMS needed some handwork before execute is not allowed after the automatic task.
      //     a. DmsMajorCheckIn
      //     b. DmsNewVersion
      //     c. DmsNoUpdateCheckIn
      for (int i=0; i<stepList.size(); i++) {
        WorkflowStep step = (WorkflowStep) stepList.get(i);
        List userActorList = step.getUserActorList();
        
        //  Check all steps is assigned with owners
        if (!WorkflowStep.ACTION_TYPE_SYSTEM_AUTO.equals(step.getActionType()) &&
            !WorkflowStep.ACTION_TYPE_FORM_DRIVEN.equals(step.getActionType()) &&
            !WorkflowStep.ACTION_TYPE_END.equals(step.getActionType()) &&
            !(WorkflowStep.ACTION_TYPE_SUBMIT.equals(step.getActionType()) && SystemWorkflowConstant.SUB_WORKFLOW_INTERRUPT.equals(String.valueOf(workflowRecord.getWorkflowCategoryID())) ) ) {
          if (Utility.isEmpty(userActorList)) {
            exceptionList.addException(new ApplicationException(WorkflowErrorConstant.NOT_ASSIGN_WITH_USER_ACTOR, null, step.getStepSeq()+". "+step.getStepName()));
          }
        } //  End check assigned with owners
        
        //  Check dynamic assign is not allowed after automatic task
        //  Previous Task Action Taker's Report To can not follow the automatic task
        //  Previous Task Action Taker's Task can not follow the automatic task
        for ( int j = 0 ; userActorList!=null && j < userActorList.size() ; j++ ) {
          MtmWorkflowStepUserActor mtmWorkflowStepUserActor = (MtmWorkflowStepUserActor)userActorList.get(j);
          // If there is a actor type ACTOR_TYPE_RUNTIME_ASSIGN in the list, it is dynamic assign owner
          boolean isDynamic = MtmWorkflowStepUserActor.ACTOR_TYPE_RUNTIME_ASSIGN.equals(mtmWorkflowStepUserActor.getActorType());
          boolean isReportTo = MtmWorkflowStepUserActor.ACTOR_TYPE_REPORT_TO_OF_PREVIOUS_TASK_ACTION_TAKER.equals(mtmWorkflowStepUserActor.getActorType());
          boolean isPreTaker = MtmWorkflowStepUserActor.ACTOR_TYPE_PREVIOUS_TASK_ACTION_TAKER.equals(mtmWorkflowStepUserActor.getActorType());
          if (isDynamic||isReportTo||isPreTaker) {
            List parentStepList = stepDAObject.getListByChildStepID(step.getID());
            for ( int k = 0 ; parentStepList!=null && k <parentStepList.size() ; k++){
              WorkflowStep tmpWorkflowStep =(WorkflowStep) parentStepList.get(k);
              
              //if the parent step(s) contains start step,add this workflowRecord is interrupt, add to exception list
              if(WorkflowStep.ACTION_TYPE_SUBMIT.equals(tmpWorkflowStep.getActionType())){
                if(isDynamic&&SystemWorkflowConstant.SUB_WORKFLOW_INTERRUPT.equals(workflowRecord.getWorkflowCategoryID().toString())){
                  exceptionList.addException(new ApplicationException(WorkflowErrorConstant.CAN_NOT_USE_DYNAMIC_ASSIGN_AFTER_SUBMIT_TASK_OF_SUB_WORKFLOW_INTERRUPT, null, step.getStepSeq()+" : "+step.getStepName()));
                }
                if(isReportTo&&SystemWorkflowConstant.SUB_WORKFLOW_INTERRUPT.equals(workflowRecord.getWorkflowCategoryID().toString())){
                  exceptionList.addException(new ApplicationException(WorkflowErrorConstant.CAN_NOT_USE_REPORT_TO_AFTER_SUBMIT_TASK_OF_SUB_WORKFLOW_INTERRUPT, null, step.getStepSeq()+" : "+step.getStepName()));
                }
                if(isPreTaker&&SystemWorkflowConstant.SUB_WORKFLOW_INTERRUPT.equals(workflowRecord.getWorkflowCategoryID().toString())){
                  exceptionList.addException(new ApplicationException(WorkflowErrorConstant.CAN_NOT_USE_PREVIOUS_TAKER_AFTER_SUBMIT_TASK_OF_SUB_WORKFLOW_INTERRUPT, null, step.getStepSeq()+" : "+step.getStepName()));
                }
                //add other check here...
              }
              
              //  If the parent step(s) contains automatic task, add to exceptio list
              if( WorkflowStep.ACTION_TYPE_SYSTEM_AUTO.equals(tmpWorkflowStep.getActionType())){
                if (isDynamic){
                  exceptionList.addException(new ApplicationException(WorkflowErrorConstant.CAN_NOT_USE_DYNAMIC_ASSIGN_AFTER_AUTOMATIC_TASK, null, step.getStepSeq()+" : "+step.getStepName()));
                }
                if (isReportTo){
                  exceptionList.addException(new ApplicationException(WorkflowErrorConstant.CAN_NOT_USE_REPORT_TO_AFTER_AUTOMATIC_TASK, null, step.getStepSeq()+" : "+step.getStepName()));
                }
              }
            }
          }
        }// End check dynamic assign is not allowed after automatic task
        
        //check dynamic assign due date is after automatic task
        WorkflowStep tmpWorkflowStep =new WorkflowStep();
        tmpWorkflowStep = (WorkflowStep)stepDAObject.getObjectByID(step.getID());
        String sDynamicDueDate = tmpWorkflowStep.getAllowAssignDueDate();
        if (sDynamicDueDate!=null){
          if (sDynamicDueDate.equals(WorkflowStep.DYNAMIC_ASSIGN_DUE_DATE_TRUE)||sDynamicDueDate.equals(WorkflowStep.DYNAMIC_ASSIGN_DUE_DATE_MANDATORY)){
            List parentStepListNew = stepDAObject.getListByChildStepID(step.getID());
            for ( int k = 0 ; parentStepListNew!=null && k <parentStepListNew.size() ; k++){
              WorkflowStep tmpWorkflowStepNew =(WorkflowStep) parentStepListNew.get(k);
              //  If the parent step(s) contains automatic task, add to exceptio list
              if( WorkflowStep.ACTION_TYPE_SYSTEM_AUTO.equals(tmpWorkflowStepNew.getActionType())){
                exceptionList.addException(new ApplicationException(WorkflowErrorConstant.CAN_NOT_USE_DYNAMIC_ASSIGN_DUE_DATE_AFTER_AUTOMATIC_TASK, null, step.getStepSeq()+" : "+step.getStepName()));
              }
            }
          }
        }
        //End check dynamic assign due date is after automatic task
        
        // Brook.Huang add start.
        // (6)Every step's due date must later than previous step.
        List parentStepList = stepDAObject.getListByChildStepID(step.getID());
        Timestamp stepDueDate = step.getOnNoResponseAfter();
        for (int j = 0; j < parentStepList.size(); j++) {
          WorkflowStep parentStep = (WorkflowStep) parentStepList.get(j);
          // Compare with workflow record's end date while parent step is start step.
          Timestamp parentStepDueDate = WorkflowStep.ACTION_TYPE_SUBMIT.equals(parentStep.getActionType()) ? workflowRecord.getStartDate() : parentStep.getOnNoResponseAfter();
      
          // Compare them while they are all not empty.
          if (!Utility.isEmpty(stepDueDate)) {
            // Add error if current step's due date earlier than previous.
            if (!Utility.isEmpty(parentStepDueDate) && stepDueDate.compareTo(parentStepDueDate) < 0) {
              if(!WorkflowStep.ACTION_TYPE_SUBMIT.equals(parentStep.getActionType())){
                /*exceptionList.addException(new ApplicationException(WorkflowErrorConstant.CAN_NOT_STEP_DUE_DATE_EARLIER_TAHN_PREVIOUS, null,
                    new String[]{step.getStepSeq()+". "+step.getStepName() + " : " + TextUtility.formatTimestamp(stepDueDate),
                    parentStep.getStepSeq()+". "+parentStep.getStepName() + " : " + TextUtility.formatTimestamp(parentStepDueDate)})); */ 
                warningList.addMessage(WorkflowErrorConstant.CAN_NOT_STEP_DUE_DATE_EARLIER_TAHN_PREVIOUS,new String[]{step.getStepSeq()+". "+step.getStepName() + " : " + TextUtility.formatTimestamp(stepDueDate),parentStep.getStepSeq()+". "+parentStep.getStepName() + " : " + TextUtility.formatTimestamp(parentStepDueDate)});
              }
            }
            if(!Utility.isEmpty(workflowRecord.getStartDate()) && stepDueDate.compareTo(workflowRecord.getStartDate()) < 0){
              exceptionList.addException(new ApplicationException(WorkflowErrorConstant.CAN_NOT_STEP_DUE_DATE_EARLIER_THAN_START_DATE, null,
                  new String[]{step.getStepSeq()+". "+step.getStepName() + " : " + TextUtility.formatTimestamp(stepDueDate),TextUtility.formatTimestamp(workflowRecord.getStartDate())}));
            }           
          }
          // Add error while privious step's due date is �� but current step isn't empty.
          /*if (!Utility.isEmpty(stepDueDate) && Utility.isEmpty(parentStepDueDate)) {
              exceptionList.addException(new ApplicationException(WorkflowErrorConstant.CAN_NOT_STEP_DUE_DATE_EARLIER_TAHN_PREVIOUS, null,
                  new String[]{step.getStepSeq()+". "+step.getStepName() + " : " + TextUtility.formatTimestamp(stepDueDate),
                  parentStep.getStepSeq()+". "+parentStep.getStepName() + " : ��"}));
           
          }*///did not need to check this condition if pre Step's DueDate is null
        }
        // Brook.Huang add end.
        
        // Brook.Huang add start.
        // (7)The auto task(s) related to DMS who needed some handwork before execute is not allowed after the automatic task.
        // e.g.
        //     a. DmsMajorCheckIn
        //     b. DmsNewVersion
        //     c. DmsNoUpdateCheckIn
        if (WorkflowStep.ACTION_TYPE_SYSTEM_AUTO.equals(step.getActionType())) {
          // Do the check if current step is one of (e.g.).
          if (isCheckInAutoTask(SystemWorkflowConstant.DMS_MAJOR_CHECK_IN_AUTO_TASK_IDS, step.getActionID().toString())
              || isCheckInAutoTask(SystemWorkflowConstant.DMS_MINOR_CHECK_IN_AUTO_TASK_IDS, step.getActionID().toString())
              || isCheckInAutoTask(SystemWorkflowConstant.DMS_NO_UPDATE_CHECK_IN, step.getActionID().toString())) {
            // Iterate parent step list, add error while parent step is one of automatic task.
            for (int j = 0; j < parentStepList.size(); j++) {
              WorkflowStep parentStep = (WorkflowStep) parentStepList.get(j);
              if (WorkflowStep.ACTION_TYPE_SYSTEM_AUTO.equals(parentStep.getActionType()) || WorkflowStep.ACTION_TYPE_SUBMIT.equals(parentStep.getActionType())) {
                exceptionList.addException(new ApplicationException(
                    WorkflowErrorConstant.CAN_NOT_CHECK_IN_STEP_AFTER_AUTOMATIC_TASK, null,
                    new String[]{step.getStepSeq()+" : "+step.getStepName(), parentStep.getStepSeq()+" : "+parentStep.getStepName()}));
              }
            }
          }
        }
        // Brook.Huang add end.
      } //  End check for each steps
      
      // Find the 1st step of the workflow.
      for (int i=0; i<stepList.size(); i++) {
        WorkflowStep step = (WorkflowStep)stepList.get(i);
        if (WorkflowStep.ACTION_TYPE_SUBMIT.equals(step.getActionType())) {
          firstStep = step;
          break;
        }
      }
      
      // Put the firstStep into tree.
      Map stepMap = Utility.getPropertyMap(stepList, "ID");
      int recurCount = 1;
      if (firstStep!=null) {
        tree.put(firstStep.getID(), null, firstStep);
        for (int i=0;i<mapList.size();i++) {
          MtmWorkflowStepWorkflowStep step = (MtmWorkflowStepWorkflowStep)mapList.get(i);
          WorkflowStep stepInMap = (WorkflowStep) stepMap.get(step.getChildStepID());
          WorkflowStep clonedStep = (WorkflowStep)stepInMap.clone();
          clonedStep.setParentType(step.getParentType());
          if (tree.get(step.getChildStepID())==null && stepMap.get(step.getChildStepID())!=null){
            tree.put(step.getChildStepID(), step.getParentStepID(), clonedStep);
          } else {
            tree.put("L_"+(recurCount++)+"_"+step.getChildStepID(), step.getParentStepID(), clonedStep);
          }
          mtmStepMap.put(step.getParentStepID()+"_"+step.getParentType()+"_"+step.getChildStepID(),step);
        }
      }
      // Build the node list.
      if (tree.getRoot()==null) {
        nodeList = new ArrayList();
      } else {
        nodeList = Collections.list(tree.getRoot().depthFirstEnumeration());
      }
      
      // Build the whole tree.
      for (int i=0; i<nodeList.size(); i++) {
        PositionTreeNode leaf = (PositionTreeNode)nodeList.get(i);
        
        if (!leaf.getID().toString().startsWith("L")) {
          WorkflowStep leafStep = (WorkflowStep)leaf.getUserObject();
          List childrenList = Collections.list(leaf.children());
          String type="";
          
          for (int k=0; k<childrenList.size(); k++) {
            WorkflowStep childStep = (WorkflowStep)((PositionTreeNode)childrenList.get(k)).getUserObject();
            Integer childStepID = childStep.getID();
            MtmWorkflowStepWorkflowStep mtmWSWS = (MtmWorkflowStepWorkflowStep)mtmStepMap.get(leafStep.getID()+"_"+childStep.getParentType()+"_"+childStep.getID());
            type += mtmWSWS.getParentType();
          }
          
          log.debug("leafStep: "+leafStep.getID()+", type=" + type +", actionType=" + leafStep.getActionType() +", num of routine: "+leafStep.getNumOfRoutine());
          //log.debug(leafStep);
          if (WorkflowStep.ACTION_TYPE_APPROVAL.equals(leafStep.getActionType())) {
            if (type.lastIndexOf("A")<0) {
              exceptionList.addException(new ApplicationException(WorkflowErrorConstant.NOT_END_WITH_END_STEP));
            }
            if (type.lastIndexOf("R")<0) {
              exceptionList.addException(new ApplicationException(WorkflowErrorConstant.NOT_END_WITH_END_STEP));
            }
          } else if (WorkflowStep.ACTION_TYPE_SYSTEM_AUTO.equals(leafStep.getActionType()) && leafStep.getNumOfRoutine() > 1) {
            int numOfRoutine = leafStep.getNumOfRoutine();
            for (int k=0; k<numOfRoutine; k++) {
              if (type.lastIndexOf("S"+k)<0) {
                exceptionList.addException(new ApplicationException(WorkflowErrorConstant.NOT_END_WITH_END_STEP));
              }
            }
          } else if (WorkflowStep.ACTION_TYPE_TASK_TO_DO.equals(leafStep.getActionType()) || WorkflowStep.ACTION_TYPE_SUBMIT.equals(leafStep.getActionType()) || (WorkflowStep.ACTION_TYPE_SYSTEM_AUTO.equals(leafStep.getActionType()) && (leafStep.getNumOfRoutine()==1))) {
            if (type.lastIndexOf("S")<0) {
              exceptionList.addException(new ApplicationException(WorkflowErrorConstant.NOT_END_WITH_END_STEP));
            }
          }
        }
      }
    } catch (ApplicationException e) {
      exceptionList.addException(e);
    } catch (Exception e) {
      log.error(e, e);
      exceptionList.addException(new ApplicationException(ErrorConstant.COMMON_FATAL_ERROR, e));
    }
    
    return(exceptionList);
  }
  
  /**
   * check the step if check in auto task.
   * @param checkInActionIDs
   * @param stepActionID
   * @return
   */
  private boolean isCheckInAutoTask(String checkInActionIDs, String stepActionID){
    String[] actionIDArr = TextUtility.splitString(checkInActionIDs,",");
    for(int i=0; i<actionIDArr.length; i++){
      if(actionIDArr[i].equals(stepActionID)){
        return true;
      }
    }
    return false;
  }
  
  /**
   * Creates a workflow progress object (a task). After the task is created,
   * notifications will also be scheduled.
   *
   * @param workflowProgress The workflow progress to be created
   * @param autoExecuteNext Whether to automatically execute the created task
   * @return The created workflow progress object
   * @throws ApplicationException
   * @see com.dcivision.workflow.bean.WorkflowStep
   * @see com.dcivision.workflow.bean.WorkflowProgress
   */
  public WorkflowProgress createWorkflowProgress(WorkflowProgress workflowProgress, boolean autoExecuteNext) throws ApplicationException {
    WorkflowProgressDAObject progressDAO = new WorkflowProgressDAObject(sessionContainer, dbConn);
    MtmWorkflowStepUserActorDAObject stepUserActorDAO = new MtmWorkflowStepUserActorDAObject(sessionContainer, dbConn);
    WorkflowProgressManager workflowProgressManager = new WorkflowProgressManager(sessionContainer, dbConn);
    WorkflowStep workflowStep = workflowProgress.getWorkflowStepSource();
    workflowProgress = (WorkflowProgress) progressDAO.insertObject(workflowProgress);
    MtmWorkflowStepUserActor stepUserActor = new MtmWorkflowStepUserActor();
    boolean actorTypeIsAssgin = false;
    
    log.debug("auto execute next? "+autoExecuteNext);
    if (!autoExecuteNext) {
      notifyArrivalByMail(workflowStep, workflowProgress);
      notifyAllOverdueByMail(workflowStep, workflowProgress);
      notifyOwnerOverdueByMail(workflowStep, workflowProgress);
      notifyArrivalBySystem(workflowStep, workflowProgress);
      notifyAllOverdueBySystem(workflowStep, workflowProgress);
      notifyOwnerOverdueBySystem(workflowStep, workflowProgress);
      
      //notify delegated person depend on delegate setep
      notifyDelegatedPersonByMail(workflowStep, workflowProgress);
      notifyDelegatedPersonBySystem(workflowStep, workflowProgress);
      
      scheduleNoResponseAction(workflowStep, workflowProgress);
    }
    
    scheduleMail(getScheduledJobVector());
    this.clearScheduledJobVector();
    //add by dick
    //create job where the notification rule is user decision
    List userActorList = stepUserActorDAO.getListByWorkflowStepID(workflowStep.getID());
    if (!Utility.isEmpty(userActorList)) {
      for(int i = 0; i < userActorList.size(); i++) {
        stepUserActor = (MtmWorkflowStepUserActor)userActorList.get(i);
        if (MtmWorkflowStepUserActor.ACTOR_TYPE_RUNTIME_ASSIGN.equals(stepUserActor.getActorType())) {
          actorTypeIsAssgin = true;
        }
      }
    }
    if (!actorTypeIsAssgin) {
      workflowProgressManager.createNotificationWhenTaskArrived(workflowProgress.getID());
    }
    return workflowProgress;
  }
  /**
   * Assigns progress owner for a dynamic assign owner step
   *
   * @param progressUserAry Array of UserRecord ID to be assigned
   * @return A List of added UserRecord ID
   * @throws ApplicationException
   * @see com.dcivision.workflow.bean.MtmWorkflowProgressUserRecord
   */
  public List createWorkflowProgressUserRecord(String[] progressUserAry) throws ApplicationException {
    MtmWorkflowProgressUserRecordDAObject progressUserDAO = new MtmWorkflowProgressUserRecordDAObject(sessionContainer, dbConn);
    
    List userRecordIDList = new ArrayList();
    Integer progressID, userRecordID;
    for (int i=0; i<progressUserAry.length; i++) {
      int delimiterIndex = progressUserAry[i].indexOf("_");
      progressID = TextUtility.parseIntegerObj(progressUserAry[i].substring(0, delimiterIndex));
      userRecordID = TextUtility.parseIntegerObj(progressUserAry[i].substring(delimiterIndex+1));
      userRecordIDList.add(userRecordID);
      log.debug("progress id: " + progressID + ", user record id: "+userRecordID);
      
      MtmWorkflowProgressUserRecord progressUser = new MtmWorkflowProgressUserRecord();
      progressUser.setWorkflowProgressID(progressID);
      progressUser.setUserRecordID(userRecordID);
      progressUser.setWorkflowAction(WorkflowProgress.STATUS_PENDING);
      
      progressUser = (MtmWorkflowProgressUserRecord) progressUserDAO.insertObject(progressUser);
    }
    return userRecordIDList;
  }
  /**
   * Schedules email notification to current step owner upon task arrival (if necessary)
   *
   * @param workflowStep The WorkflowStep object of the current progress
   * @param workflowProgress The current progress object
   * @throws ApplicationException
   */
  public void notifyArrivalByMail(WorkflowStep workflowStep, WorkflowProgress workflowProgress) throws ApplicationException {
    log.debug("workflow step isNotifyOwnerTaskArrival(): "+workflowStep.isNotifyOwnerTaskArrivalByMail());
    log.debug("before scheduledJobVec.size() = " + scheduledJobVec.size());
    if (workflowStep.isNotifyOwnerTaskArrivalByMail()) {
      java.util.Vector tmpVector = new java.util.Vector();
      tmpVector.add(workflowStep);
      tmpVector.add(workflowProgress);
      tmpVector.add(WorkflowStep.NOTIFY_OWNER_TASK_ARRIVED_BY_MAIL);
      this.scheduledJobVec.add(tmpVector);
      log.debug("after scheduledJobVec.size() = " + scheduledJobVec.size());
    }
  }
  /**
   * Schedules system notification to current step owner upon task arrival (if necessary)
   *
   * @param workflowStep The WorkflowStep object of the current progress
   * @param workflowProgress The current progress object
   * @throws ApplicationException
   */
  public void notifyArrivalBySystem(WorkflowStep workflowStep, WorkflowProgress workflowProgress) throws ApplicationException {
    log.debug("workflow step isNotifyOwnerTaskArrival(): "+workflowStep.isNotifyOwnerTaskArrivalBySystem());
    log.debug("before scheduledJobVec.size() = " + scheduledJobVec.size());
    if (workflowStep.isNotifyOwnerTaskArrivalBySystem()) {
      java.util.Vector tmpVector = new java.util.Vector();
      tmpVector.add(workflowStep);
      tmpVector.add(workflowProgress);
      tmpVector.add(WorkflowStep.NOTIFY_OWNER_TASK_ARRIVED_BY_SYSTEM);
      this.scheduledJobVec.add(tmpVector);
      log.debug("after scheduledJobVec.size() = " + scheduledJobVec.size());
    }
  }
  /**
   * Schedules email notification to ALL step owner upon task overdued (if necessary)
   *
   * @param workflowStep The WorkflowStep object of the current progress
   * @param workflowProgress The current progress object
   * @throws ApplicationException
   */
  public void notifyAllOverdueByMail(WorkflowStep workflowStep, WorkflowProgress workflowProgress) throws ApplicationException {
    log.debug("before scheduledJobVec.size() = " + scheduledJobVec.size());
    if (workflowStep.isNotifyAllTaskOverdueByMail()) {
      java.util.Vector tmpVector = new java.util.Vector();
      tmpVector.add(workflowStep);
      tmpVector.add(workflowProgress);
      tmpVector.add(WorkflowStep.NOTIFY_ALL_TASK_OVERDUE_BY_MAIL);
      this.scheduledJobVec.add(tmpVector);
      log.debug("scheduledJobVec.size() = " + scheduledJobVec.size());
    }
  }
  /**
   * Schedules system notification to ALL step owner upon task overdued (if necessary)
   *
   * @param workflowStep The WorkflowStep object of the current progress
   * @param workflowProgress The current progress object
   * @throws ApplicationException
   */
  public void notifyAllOverdueBySystem(WorkflowStep workflowStep, WorkflowProgress workflowProgress) throws ApplicationException {
    log.debug("before scheduledJobVec.size() = " + scheduledJobVec.size());
    if (workflowStep.isNotifyAllTaskOverdueBySystem()) {
      java.util.Vector tmpVector = new java.util.Vector();
      tmpVector.add(workflowStep);
      tmpVector.add(workflowProgress);
      tmpVector.add(WorkflowStep.NOTIFY_ALL_TASK_OVERDUE_BY_SYSTEM);
      this.scheduledJobVec.add(tmpVector);
      log.debug("scheduledJobVec.size() = " + scheduledJobVec.size());
    }
  }
  /**
   * Schedules email notification to current step owner upon task overdued (if necessary)
   *
   * @param workflowStep The WorkflowStep object of the current progress
   * @param workflowProgress The current progress object
   * @throws ApplicationException
   */
  public void notifyOwnerOverdueByMail(WorkflowStep workflowStep, WorkflowProgress workflowProgress) throws ApplicationException {
    if (workflowStep.isNotifyOwnerTaskOverdueByMail()) {
      java.util.Vector tmpVector = new java.util.Vector();
      tmpVector.add(workflowStep);
      tmpVector.add(workflowProgress);
      tmpVector.add(WorkflowStep.NOTIFY_OWNER_TASK_OVERDUE_BY_MAIL);
      this.scheduledJobVec.add(tmpVector);
    }
  }
  /**
   * Schedules system notification to current step owner upon task overdued (if necessary)
   *
   * @param workflowStep The WorkflowStep object of the current progress
   * @param workflowProgress The current progress object
   * @throws ApplicationException
   */
  public void notifyOwnerOverdueBySystem(WorkflowStep workflowStep, WorkflowProgress workflowProgress) throws ApplicationException {
    if (workflowStep.isNotifyOwnerTaskOverdueBySystem()) {
      java.util.Vector tmpVector = new java.util.Vector();
      tmpVector.add(workflowStep);
      tmpVector.add(workflowProgress);
      tmpVector.add(WorkflowStep.NOTIFY_OWNER_TASK_OVERDUE_BY_SYSTEM);
      this.scheduledJobVec.add(tmpVector);
    }
  }
  /**
   * Schedules email notification to current step owner upon task completed (if necessary)
   *
   * @param workflowStep The WorkflowStep object of the current progress
   * @param workflowProgress The current progress object
   * @throws ApplicationException
   */
  public void notifyCompletionByMail(WorkflowStep workflowStep, WorkflowProgress workflowProgress) throws ApplicationException {
    if (workflowStep.isNotifyAllTaskCompletedByMail()) {
      java.util.Vector tmpVector = new java.util.Vector();
      tmpVector.add(workflowStep);
      tmpVector.add(workflowProgress);
      tmpVector.add(WorkflowStep.NOTIFY_ALL_TASK_COMPLETED_BY_MAIL);
      this.scheduledJobVec.add(tmpVector);
    }
  }
  /**
   * Schedules system notification to current step owner upon task completed (if necessary)
   *
   * @param workflowStep The WorkflowStep object of the current progress
   * @param workflowProgress The current progress object
   * @throws ApplicationException
   */
  public void notifyCompletionBySystem(WorkflowStep workflowStep, WorkflowProgress workflowProgress) throws ApplicationException {
    if (workflowStep.isNotifyAllTaskCompletedBySystem()) {
      java.util.Vector tmpVector = new java.util.Vector();
      tmpVector.add(workflowStep);
      tmpVector.add(workflowProgress);
      tmpVector.add(WorkflowStep.NOTIFY_ALL_TASK_COMPLETED_BY_SYSTEM);
      this.scheduledJobVec.add(tmpVector);
    }
  }
  /**
   * Adds the temporary notification list to the main notification schedule
   *
   * @param tmpVector Vector of the general (not rule-based) notification list
   * @throws ApplicationException
   */
  public void addScheduleJobVecForNotifiyRule(java.util.Vector tmpVector) throws ApplicationException {
    this.scheduledJobVec.add(tmpVector);
  }
  /**
   * Stops the workflow track of the given progress and schedule for notification of the stop
   *
   * @param workflowProgress The specific progress
   * @param workflowStep The workflow step object of the progress
   * @throws ApplicationException
   * @see com.dcivision.workflow.dao.WorkflowProgressDAObject#stopWorkflowTrackByTrackID(Integer)
   */
  public void stopWorkflowTrack(WorkflowProgress workflowProgress, WorkflowStep workflowStep) throws ApplicationException {
    log.debug("stopping workflow track");
    WorkflowProgressDAObject progressDAO = new WorkflowProgressDAObject(sessionContainer, dbConn);
    WorkflowOperationManager wfOperationManager = new WorkflowOperationManager(sessionContainer, dbConn);
    WorkflowRecordDAObject recordDAO = new WorkflowRecordDAObject(sessionContainer, dbConn);
    MtmWorkflowProgressSystemObjectDAObject systemObjectDAO = new MtmWorkflowProgressSystemObjectDAObject(sessionContainer, dbConn);
    FormSubmissionDAObject formSubmissionDAO = new FormSubmissionDAObject(sessionContainer,dbConn);
//  added by nancy
    MtmWorkflowProgressUserRecordDAObject progressUserRecordDAO = new MtmWorkflowProgressUserRecordDAObject(sessionContainer, dbConn);
    List tempList=progressUserRecordDAO.getListByWorkflowProgressID(workflowProgress.getID());
    
    WorkflowProgress wfProgress=(WorkflowProgress)progressDAO.getObjectByID(workflowProgress.getID());
    if((GlobalConstant.TRUE).equals(wfProgress.getCancelFlag())){
       throw new ApplicationException(ErrorConstant.WORKFLOW_TRACK_ALREADY_STOPPED );
    }
    /*if((GlobalConstant.TRUE).equals(wfProgress.getCompleteFlag())){
      throw new ApplicationException(ErrorConstant.WORKFLOW_TRACK_ALREADY_COMPLETED );
    }*/
   
    //end
    
//  add by dick
    //update bind form is status where workflow is complete
    WorkflowRecord tempRecord = (WorkflowRecord)recordDAO.getObjectByID(workflowStep.getWorkflowRecordID());
    if (SystemWorkflowConstant.FMB_SUBMISSION_BY_FORM.equals(tempRecord.getWorkflowCategoryID().toString())
    || SystemWorkflowConstant.FMB_SUBMISSION_BY_GROUP.equals(tempRecord.getWorkflowCategoryID().toString()) ) {
      List systemObjectList = systemObjectDAO.getListByBeanClassNameTrackID("com.dcivision.form.bean.FormSubmission", workflowProgress.getTrackID());
      if (!Utility.isEmpty(systemObjectList)){
        MtmWorkflowProgressSystemObject systemObject =(MtmWorkflowProgressSystemObject)systemObjectList.get(0); 
        FormSubmission formSubmission = (FormSubmission) formSubmissionDAO.getObjectByID(systemObject.getObjectID());
        formSubmission.setStatus(FormSubmission.FORM_SUBMISSION_STATU_CANCEL);
        formSubmissionDAO.updateObject(formSubmission);
      }
    }
    //end
    //progressDAO.stopWorkflowTrackByTrackID(workflowProgress.getTrackID());
    //progressDAO.cancelWorkflowByTrackID(workflowProgress.getTrackID());//simon_del
    progressDAO.cancelWorkflowByProgressID(workflowProgress.getID());//simon_add
    workflowProgress.setUpdaterID(sessionContainer.getUserRecordID());//simon_add    
    
    WorkflowProgressManager workflowProgressManager = new WorkflowProgressManager(sessionContainer, dbConn);
    workflowProgressManager.createNotificationWhenTaskStoped(workflowProgress.getID());
  }
  /**
   * Schedules overdue action, if the due date of the given workflow progress/step is 
   * not specified, this method will do nothing.
   * 
   * It will store the given WorkflowStep and WorkflowProgress object to a JobDataMap
   * for use in the schedule job
   *
   * @param workflowStep - The workflow step object
   * @param workflowProgress - The current progress
   * @throws ApplicationException
   * @see com.dcivision.workflow.core.NoResponseWorkflowHandler
   * @see org.quartz.JobDataMap
   */
  public void scheduleNoResponseAction(WorkflowStep workflowStep, WorkflowProgress workflowProgress) throws ApplicationException {
    Scheduler sched;
    try{
      log.info("-=-=-=-=-=-=-scheduleNoReponseAction-=-=-=-=-=-=-=-=");
      log.info("workflow step ID: "+ workflowStep.getID() +"\n workflow progress ID: "+workflowProgress.getID());
      Integer onNoResponseFor = workflowStep.getOnNoResponseFor();
      Timestamp onNoResponseAfter = workflowStep.getOnNoResponseAfter();
      log.debug("On No Response For: "+onNoResponseFor);
      log.debug("On No Response After: "+onNoResponseAfter);
      String actionType = workflowStep.getOnNoResponseAction();
      sched = SchedulerFactory.getScheduler();
      
      JobDataMap dataMap = new JobDataMap();
      dataMap.put("WORKFLOW_STEP", workflowStep);
      dataMap.put("WORKFLOW_PROGRESS", workflowProgress);
      JobDetail job = new JobDetail(GlobalConstant.JOB_PREFIX_WORKFLOW_NORESPONSE+workflowProgress.getID(), GlobalConstant.JOB_PREFIX_WORKFLOW_NORESPONSE+"GROUP", NoResponseWorkflowHandler.class);
      job.setJobDataMap(dataMap);
      
      //calculate the schedule date
      Timestamp scheduleDate = null;
      if ((GlobalConstant.TRUE.equals(workflowStep.getAllowAssignDueDate()) || WorkflowStep.DYNAMIC_ASSIGN_DUE_DATE_MANDATORY.equals(workflowStep.getAllowAssignDueDate())) && !Utility.isEmpty(workflowProgress.getDueDate()) ){
        scheduleDate = workflowProgress.getDueDate();
      } else if (onNoResponseFor!=null){
        scheduleDate = Utility.addDay(workflowProgress.getCreateDate(),onNoResponseFor.intValue());
        // Schedule the job a delay to avoid the case that the WorkflowProgress is not created due to connection not committed
        if(onNoResponseFor.intValue()==0){
          Calendar tmpCal = Utility.timestampToCalendar(scheduleDate);
          tmpCal.add(java.util.Calendar.SECOND, TextUtility.parseInteger(SystemParameterFactory.getSystemParameter(SystemParameterConstant.WORKFLOW_AUTOMATIC_TASK_DELAY_TIME)));
          scheduleDate = Utility.calendarToTimestamp(tmpCal);
        }
      } else if (onNoResponseAfter!=null){
        scheduleDate = onNoResponseAfter;
      }
      
      if (scheduleDate!=null){
        
        if(scheduleDate.before(Utility.getCurrentTimestamp())){
          //if scheduleDate<currentDate,schedule will fire now,so need reSet the schedule Time
          //if schedule fire now, connection will do not commit yet,it will couse some db error
          //calculate the schedule date tc.add for bug-1880
          int delay = TextUtility.parseInteger(SystemParameterFactory.getSystemParameter(SystemParameterConstant.WORKFLOW_AUTOMATIC_TASK_DELAY_TIME));
          scheduleDate = new Timestamp(Utility.getCurrentTimestamp().getTime() + (delay + (workflowProgress.getID().intValue())%10 ) * 1000L);
        }
        
        log.info("schedule date: "+ scheduleDate.toString());
        Timestamp tmpScheduleDate = new Timestamp(scheduleDate.getTime()+1000);//For BUG-1093
        SimpleTrigger trigger = new SimpleTrigger(GlobalConstant.JOB_PREFIX_WORKFLOW_NORESPONSE+workflowProgress.getID(), GlobalConstant.JOB_PREFIX_WORKFLOW_NORESPONSE+"GROUP", GlobalConstant.JOB_PREFIX_WORKFLOW_NORESPONSE+workflowProgress.getID(), GlobalConstant.JOB_PREFIX_WORKFLOW_NORESPONSE+"GROUP", tmpScheduleDate, null, 0, 0);
        trigger.setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW);
        log.info("trigger created");
        
        log.info(job.getFullName() + " will run at: " + trigger.getNextFireTime() + " & repeat: " + trigger.getRepeatCount() + "/" + trigger.getRepeatInterval());
        sched.scheduleJob(job, trigger);
        log.info("Add schedule for wprogressID="+workflowProgress.getID()+" finished!");
      }
    }catch(Exception e){
      log.error(e, e);
      throw new ApplicationException(e);
    }finally{
      log.info("-=-=-=-=-=-=-END scheduleNoReponseAction-=-=-=-=-=-=-=-=");
    }
  }
  /**
   * Creates workflow task attachment uploaded by the specified progress user record
   *
   * @param attachmentList List of WorkflowProgressAttachment object
   * @param progressUserID The MtmWorkflowProgressUserRecord who complete the task and upload attachments
   * @throws ApplicationException
   * @see com.dcivision.workflow.bean.WorkflowProgressAttachment
   * @see com.dcivision.workflow.bean.MtmWorkflowProgressUserRecord
   */
  public void createWorkflowProgressAttachments(List attachmentList, Integer progressUserID) throws ApplicationException {
    if (attachmentList == null) {
      return;
    }
    
    log.debug("insert workflowProgressAttachment ");
    WorkflowProgressAttachmentDAObject progressAttachmentDAO = new WorkflowProgressAttachmentDAObject(sessionContainer, dbConn);
    
    //if the navMode = E, delete the old attachment files first
    List oldAttachemntList = progressAttachmentDAO.getListByMtmWorkflowProgressUserRecordID(progressUserID);
    if( !Utility.isEmpty(oldAttachemntList) ){
      progressAttachmentDAO.deleteByMtmWorkflowRpogressUserRecordID(progressUserID.toString());  
    }
    
    //insert the attachment file.
    for (int i=0; i <attachmentList.size(); i++) {
      WorkflowProgressAttachment progressAttachment = (WorkflowProgressAttachment) attachmentList.get(i);
      log.debug("progressAttachment: "+progressAttachment.getAttachmentName());
      progressAttachment.setMtmWorkflowProgressUserRecordID(progressUserID);
      //* insert the record
      progressAttachmentDAO.insertObject(progressAttachment);
      
      //* if the type is single attachment, need to store the file to the server
      if ( WorkflowProgressAttachment.ATTACHED_SINGLE_FILE.equals(progressAttachment.getAttachmentType()) ) {
        WorkflowContentStoreHandler contentStoreHandler = null;
        try {
          contentStoreHandler = (WorkflowContentStoreHandler)Class.forName(SystemParameterFactory.getSystemParameter(SystemParameterConstant.WORKFLOW_CONTENT_STORE_CLASS)).newInstance();
        } catch (Exception e) {
          throw new ApplicationException(WorkflowErrorConstant.CONTENT_HANDLER_CLASS_NOT_FOUND);
        }
        File file = new File(this.getStorageLocation() +  "/" + progressAttachment.getExternalID());
        if (!file.exists()) {
          contentStoreHandler.setProperty(WorkflowContentStoreHandler.FILE_PHYSICAL_PATH, this.getStorageLocation()+"/");
          contentStoreHandler.setProperty(WorkflowContentStoreHandler.FILE_NAME_PROPERTY, progressAttachment.getExternalID());
          contentStoreHandler.setProperty(WorkflowContentStoreHandler.FILE_CONTENT_PROPERTY, progressAttachment.getDataStream());
          contentStoreHandler.write();
          contentStoreHandler.close();
        }
      }
    }
  }
  
  /**
   * Gets the storage location for workflow files
   *
   * @return The phyiscal location where files uploaded for workflow are located.
   * @throws ApplicationException
   */
  public String getStorageLocation() throws ApplicationException {
    String attachmentRootFolder = SystemParameterFactory.getSystemParameter(SystemParameterConstant.WORKFLOW_ATTACHMENT_PHYSICAL_PATH);
    File rootFolder = new File(attachmentRootFolder);
    if (!rootFolder.exists()) {
      rootFolder.mkdirs();
    }
    return attachmentRootFolder;
  }
  
  /**
   * Completes the given workflow track, and thus all the pending progress.
   *
   * @param trackID The track id of the workflow to be completed
   * @throws ApplicationException
   */
  public void completeWorkflowTrack(Integer trackID) throws ApplicationException {
    WorkflowProgressDAObject progressDAO = new WorkflowProgressDAObject(sessionContainer, dbConn);
    WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(sessionContainer, dbConn);
    WorkflowRecordDAObject recordDAO = new WorkflowRecordDAObject(sessionContainer, dbConn);
    MtmWorkflowProgressSystemObjectDAObject systemObjectDAO = new MtmWorkflowProgressSystemObjectDAObject(sessionContainer, dbConn);
    FormSubmissionDAObject formSubmissionDAO = new FormSubmissionDAObject(sessionContainer,dbConn);
//  added by nancy
    List progressList = progressDAO.getListByTrackID(trackID);
    WorkflowProgress tempProgress  = (WorkflowProgress) progressList.get(0);
    
    if((GlobalConstant.TRUE).equals(tempProgress.getCompleteFlag())){
      throw new ApplicationException(ErrorConstant.WORKFLOW_TRACK_ALREADY_COMPLETED );
    }
    if((GlobalConstant.TRUE).equals(tempProgress.getCancelFlag())){
      throw new ApplicationException(ErrorConstant.WORKFLOW_TRACK_ALREADY_STOPPED );
    }
    //end
    //add by dick
    //update bind form is status where workflow is complete
    WorkflowStep tempStep = (WorkflowStep)stepDAO.getObjectByID(tempProgress.getWorkflowStepID());
    WorkflowRecord tempRecord = (WorkflowRecord)recordDAO.getObjectByID(tempStep.getWorkflowRecordID());
    if (SystemWorkflowConstant.FMB_SUBMISSION_BY_FORM.equals(tempRecord.getWorkflowCategoryID().toString())
    || SystemWorkflowConstant.FMB_SUBMISSION_BY_GROUP.equals(tempRecord.getWorkflowCategoryID().toString()) ) {
      List systemObjectList = systemObjectDAO.getListByBeanClassNameTrackID("com.dcivision.form.bean.FormSubmission", trackID);
      if (!Utility.isEmpty(systemObjectList)){
        MtmWorkflowProgressSystemObject systemObject =(MtmWorkflowProgressSystemObject)systemObjectList.get(0); 
        FormSubmission formSubmission = (FormSubmission) formSubmissionDAO.getObjectByID(systemObject.getObjectID());
        formSubmission.setStatus(FormSubmission.FORM_SUBMISSION_STATU_COMPLETED);
        formSubmissionDAO.updateObject(formSubmission);
      }
    }
    //end
   // WorkflowProgress endProgress= (WorkflowProgress) progressList.get((progressList.size())-1);

    for (int i=0; i<progressList.size(); i++) {
      WorkflowProgress progress = (WorkflowProgress) progressList.get(i);
      if (WorkflowProgress.STATUS_PENDING.equals(progress.getStatus())) {
        progress.setStatus(WorkflowProgress.STATUS_GENERAL_ACCOMPLISHMENT);
      }
      progress.setCompleteFlag(GlobalConstant.TRUE);
      //*******************************************
      // Commented by Lun at (2005-01-22)
      // This updateObject will update all the updater id to the
      // current user (sessionContainer.getUserRecordID )
      // *******************************************
      //      progressDAO.updateObject(progress);
      // *******************************************
      // End of comment
      // *******************************************
      progressDAO.setCompleteFlag(progress.getID());
    }
  }
  /**
   * Creates a WorkflowStep object by the given step. For building the step relationship,
   * the parent step ID should be specified, or otherwise the creation will be failed.
   * The rule-based notifications and priority escalation will not be created here, you
   * may need to call the according Data Access Object's insert method explicitly.
   *
   * @param step The step to be created
   * @param betweenStep Specify create the step between two step of as a parallel step
   * @return The created WorkflowStep
   * @throws ApplicationException
   * @see com.dcivision.bean.MtmWorkflowStepUserActor
   */
  public WorkflowStep createWorkflowStep(WorkflowStep step, boolean betweenStep) throws ApplicationException{
    /*WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(sessionContainer, dbConn);
    MtmWorkflowStepUserActorDAObject stepActorDAO = new MtmWorkflowStepUserActorDAObject(sessionContainer, dbConn);
    MtmWorkflowStepWorkflowStepDAObject stepStepDAO = new MtmWorkflowStepWorkflowStepDAObject(sessionContainer, dbConn);
    WorkflowActionDAObject actionDAO = new WorkflowActionDAObject(sessionContainer, dbConn);
    WorkflowStepFormEquatDAObject formEquatDAO = new WorkflowStepFormEquatDAObject(sessionContainer, dbConn);
    WorkflowStep endStep = (WorkflowStep)stepDAO.getEndStepByWorkflowRecordID(step.getWorkflowRecordID());
    
    //get the end step ID
    Integer endStepID = null;
    List stepList = stepDAO.getListByWorkflowRecordID(step.getWorkflowRecordID());
    for(int i=0; i<stepList.size(); i++){
      WorkflowStep tmpStep = (WorkflowStep)stepList.get(i);
      if(WorkflowStep.END_STEP_SEQ_NO.equals(tmpStep.getStepSeq())){
        endStepID = tmpStep.getID();
      }
    }
    
    //log.debug("before insert, step : "+step);
    //get the routineListValue
    String routeListValue = step.getRouteListValue();
    String[] routineArr = null;
    if(!Utility.isEmpty(routeListValue) && !"null".equals(routeListValue)){
      routineArr = TextUtility.splitString(routeListValue,"|");
    }
    
    String actionType = step.getActionType();
    if (actionType!=null && actionType.length() > 1) {
      String sActionID = actionType.substring(1);
      step.setActionType(actionType.substring(0, 1));
      step.setActionID(TextUtility.parseIntegerObj(sActionID));
    }
    
    StringBuffer sNotifyStr = new StringBuffer();
    StringBuffer sOverdueStr = new StringBuffer();
    if (step.isNotifyOwnerTaskArrivalByMail()) {
      sNotifyStr.append(step.getNotifyOwnerTaskArrivalByMail());
    }
    if (step.isNotifyOwnerTaskArrivalBySystem()) {
      sNotifyStr.append(step.getNotifyOwnerTaskArrivalBySystem());
    }
    if (step.isNotifyAllTaskCompletedByMail()) {
      sNotifyStr.append(step.getNotifyAllTaskCompletedByMail());
    }
    if (step.isNotifyAllTaskCompletedBySystem()) {
      sNotifyStr.append(step.getNotifyAllTaskCompletedBySystem());
    }
    step.setOnCompleteAckMethod(sNotifyStr.toString());
    
    if (step.isNotifyOwnerTaskOverdueByMail()) {
      sOverdueStr.append(step.getNotifyOwnerTaskOverdueByMail());
    }
    if (step.isNotifyOwnerTaskOverdueBySystem()) {
      sOverdueStr.append(step.getNotifyOwnerTaskOverdueBySystem());
    }
    if (step.isNotifyAllTaskOverdueByMail()) {
      sOverdueStr.append(step.getNotifyAllTaskOverdueByMail());
    }
    if (step.isNotifyAllTaskOverdueBySystem()) {
      sOverdueStr.append(step.getNotifyAllTaskOverdueBySystem());
    }
    step.setOnNoResponseAckMethod(sOverdueStr.toString());
    // check the filter by
    
    step = (WorkflowStep) stepDAO.insertObject(step);
    
    log.debug("after insert, step : "+step);
    
    //create user actor for the step
    String permData = TextUtility.replaceString(step.getAllPermissionData(), "\r", "");
    String[] tmpAry = TextUtility.splitString(permData, "\n");
    if (tmpAry == null) {
      tmpAry = new String[0];
    }
    //* specific user account
    for (int i = 0; i < tmpAry.length; i++) {
      if (!Utility.isEmpty(tmpAry[i])) {
        String[] rowAry = TextUtility.splitString(tmpAry[i], "\t");
        MtmWorkflowStepUserActor mtmWorkflowStepUserActor = new MtmWorkflowStepUserActor();
        mtmWorkflowStepUserActor.setWorkflowStepID(step.getID());
        if (MtmWorkflowStepUserActor.ACTOR_TYPE_GROUP.equals(rowAry[2]) || MtmWorkflowStepUserActor.ACTOR_TYPE_ROLE.equals(rowAry[2]) || MtmWorkflowStepUserActor.ACTOR_TYPE_USER.equals(rowAry[2])){
          mtmWorkflowStepUserActor.setActorType(rowAry[2]);
          mtmWorkflowStepUserActor.setActorID(TextUtility.parseIntegerObj(rowAry[0]));
        } else {
          mtmWorkflowStepUserActor.setActorType(rowAry[0].substring(0,1));
          if(MtmWorkflowStepUserActor.ACTOR_TYPE_ACTION_TAKER.equals(rowAry[0].substring(0,1)) ||
              MtmWorkflowStepUserActor.ACTOR_TYPE_REPORT_TO_OF_ACTION_TAKER.equals(rowAry[0].substring(0,1))){
            mtmWorkflowStepUserActor.setActorID( new Integer(rowAry[0].substring(1)));
          }else{
            mtmWorkflowStepUserActor.setActorID(new Integer(0));
          }
        }
        mtmWorkflowStepUserActor.setPermission(rowAry[3]);
        mtmWorkflowStepUserActor.setActionType(MtmWorkflowStepUserActor.ACTION_TYPE_EXECUTION);
//        if(MtmWorkflowStepUserActor.ACTOR_TYPE_RUNTIME_ASSIGN.equals(step.getAssignActorType())){  // falcon_delete
//         mtmWorkflowStepUserActor.setRecordStatus(GlobalConstant.RECORD_STATUS_PENDING);   
          mtmWorkflowStepUserActor.setRecordStatus(GlobalConstant.RECORD_STATUS_ACTIVE);     // falcon_add
//        }
        stepActorDAO.insertObject(mtmWorkflowStepUserActor);
      }
    }
    
    // add the assign at runtime actor type
    String assignActorType = step.getAssignActorType();
    if (!Utility.isEmpty(assignActorType)) {
      MtmWorkflowStepUserActor mtmWorkflowStepUserActor = new MtmWorkflowStepUserActor();
      mtmWorkflowStepUserActor.setWorkflowStepID(step.getID());
      mtmWorkflowStepUserActor.setActorType(assignActorType);
      mtmWorkflowStepUserActor.setActorID(new Integer(0));
      mtmWorkflowStepUserActor.setActionType(MtmWorkflowStepUserActor.ACTION_TYPE_EXECUTION);
      stepActorDAO.insertObject(mtmWorkflowStepUserActor);
    }
    
    
    //* create workflow steps relation
    
    //* find group id
    log.debug("add workflow step workflow step record ");
    Integer nextGroupID = stepStepDAO.getNextGroupIDForWorkflowRecord(step.getWorkflowRecordID());
    if (nextGroupID == null) {
      nextGroupID = new Integer(0);
    }
    int groupCount = nextGroupID.intValue();
    String[] parentStepAry = step.getParentStepIDs();
    MtmWorkflowStepWorkflowStep oldStepStep = null;
    String parentType = null;
    String parentStepID = null;
    Integer oldChildStepID = null;
    String oldLoopBack=null;//TC ADD FOR:if have old parent-child ship,currentStep-Child ship = old parent-child ship
    for (int i = 0; i < parentStepAry.length; i++) {
      //MtmWorkflowStepWorkflowStep mtmWorkflowStepWorkflowStep = new MtmWorkflowStepWorkflowStep();
      parentType = parentStepAry[i].substring(0,parentStepAry[i].indexOf("-"));
      //String parentType = step.getParentType();
      parentStepID = parentStepAry[i].substring(parentStepAry[i].indexOf("-")+1);
      log.debug("parent type: "+parentType);
      log.debug("parent step id: "+parentStepID);
      if(betweenStep){
        oldStepStep = (MtmWorkflowStepWorkflowStep) stepStepDAO.getParentStepToChildStepRecord(step.getWorkflowRecordID(), TextUtility.parseIntegerObj(parentStepID),parentType, step.getCurrentStepID());
      } else {
        oldStepStep = (MtmWorkflowStepWorkflowStep) stepStepDAO.getParentStepToEndStepRecord(step.getWorkflowRecordID(), parentType, TextUtility.parseIntegerObj(parentStepID));
      }
      
      log.debug("the frist oldchildStr is "+oldChildStepID );
      if (oldStepStep!= null) {
        oldChildStepID = oldStepStep.getChildStepID();
        oldLoopBack = oldStepStep.getLoopBack();//TC.ADD for currentStep-child ship next
        oldStepStep.setParentType(parentType);
        oldStepStep.setParentStepID(TextUtility.parseIntegerObj(parentStepID));
        oldStepStep.setChildStepID(step.getID());
        oldStepStep.setGroupID(nextGroupID);
        oldStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
        oldStepStep = (MtmWorkflowStepWorkflowStep) stepStepDAO.updateObject(oldStepStep);
      } else {
        MtmWorkflowStepWorkflowStep oldStepStepCreate = new MtmWorkflowStepWorkflowStep();
        oldStepStepCreate.setParentType(parentType);
        oldStepStepCreate.setParentStepID(TextUtility.parseIntegerObj(parentStepID));
        oldStepStepCreate.setChildStepID(step.getID());
        oldStepStepCreate.setGroupID(nextGroupID);
        oldStepStepCreate.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
        oldStepStepCreate = (MtmWorkflowStepWorkflowStep) stepStepDAO.insertObject(oldStepStepCreate);
      }
    }
    log.debug("the frist oldchildStr is "+oldChildStepID );
    //get oldStepStep by steptype
    
    if (oldStepStep!=null) {
      MtmWorkflowStepWorkflowStep newStepStep = new MtmWorkflowStepWorkflowStep();
      //Auto task
      if (WorkflowStep.ACTION_TYPE_SYSTEM_AUTO.equals(step.getActionType()) ) {
        WorkflowAction tmpAction = (WorkflowAction) actionDAO.getObjectByID(step.getActionID());
        WorkflowProcessor processor = AutomaticTaskFactory.getInstance(tmpAction);//Edit by TC for AutomaticTask HotDeploy
        int totalRoutine = processor.getNumOfRoutine();
        
        if (totalRoutine>1) {
          for (int j=0; j<totalRoutine; j++) {
            newStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE + j);
            newStepStep.setParentStepID(step.getID());
            newStepStep.setChildStepID(oldChildStepID);
            newStepStep.setGroupID(new Integer(++groupCount));
            //newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
            //WHY = LOOP_BACK_NO? THERE IS NO REASON ##TC.ADD START
            if(j==0)
              newStepStep.setLoopBack(oldLoopBack);
            else
              newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
            //TC.ADD END
            stepStepDAO.insertObject(newStepStep);
          }
        } else {
          newStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE);
          newStepStep.setParentStepID(step.getID());
          newStepStep.setChildStepID(oldChildStepID);
          newStepStep.setGroupID(new Integer(++groupCount));
          //newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
          newStepStep.setLoopBack(oldLoopBack);//TC.ADD
          stepStepDAO.insertObject(newStepStep);
        }
      } else if (WorkflowStep.ACTION_TYPE_APPROVAL.equals(step.getActionType())) {
        newStepStep.setParentType(WorkflowStep.PARENT_TYPE_APPROVED);
        newStepStep.setParentStepID(step.getID());
        newStepStep.setGroupID(new Integer(++groupCount));
        if(WorkflowStep.PARENT_TYPE_APPROVED.equals(step.getFollowBranch())){
          newStepStep.setChildStepID(oldChildStepID);
          //newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);//simon_del
          //newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_YES);//simon_add
          newStepStep.setLoopBack(oldLoopBack);//TC.ADD
        } else {
          newStepStep.setChildStepID(endStepID);
          newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_YES);
        }
        stepStepDAO.insertObject(newStepStep);
        
        newStepStep = new MtmWorkflowStepWorkflowStep();
        newStepStep.setParentType(WorkflowStep.PARENT_TYPE_REJECTED);
        newStepStep.setParentStepID(step.getID());
        newStepStep.setGroupID(new Integer(++groupCount));
        if(WorkflowStep.PARENT_TYPE_REJECTED.equals(step.getFollowBranch())){
          newStepStep.setChildStepID(oldChildStepID);
          //newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);//simon_del
          //newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_YES);//simon_add
          newStepStep.setLoopBack(oldLoopBack);
        } else {
          newStepStep.setChildStepID(endStepID);
          newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_YES);
        }
        stepStepDAO.insertObject(newStepStep);
        //Yes / No Task
      } else if (WorkflowStep.ACTION_TYPE_BOOLEAN.equals(step.getActionType())) {
        newStepStep.setParentType(WorkflowStep.PARENT_TYPE_YES);
        newStepStep.setParentStepID(step.getID());
        newStepStep.setGroupID(new Integer(++groupCount));
        if(WorkflowStep.PARENT_TYPE_YES.equals(step.getFollowBranch())){
          newStepStep.setChildStepID(oldChildStepID);
          //newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
          newStepStep.setLoopBack(oldLoopBack);//TC.ADD
        } else {
          newStepStep.setChildStepID(endStepID);
          newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_YES);
        }
        stepStepDAO.insertObject(newStepStep);
        
        newStepStep = new MtmWorkflowStepWorkflowStep();
        newStepStep.setParentType(WorkflowStep.PARENT_TYPE_NO);
        newStepStep.setParentStepID(step.getID());
        newStepStep.setGroupID(new Integer(++groupCount));
        if(WorkflowStep.PARENT_TYPE_NO.equals(step.getFollowBranch())){
          newStepStep.setChildStepID(oldChildStepID);
          //newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
          newStepStep.setLoopBack(oldLoopBack);//TC.ADD
        } else {
          newStepStep.setChildStepID(endStepID);
          newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_YES);
        }
        stepStepDAO.insertObject(newStepStep);
        //Form Value Driven Task
      } else if (WorkflowStep.ACTION_TYPE_FORM_DRIVEN.equals(step.getActionType())) {
        String formEquationListValue = step.getFormEquationListValue();
        String[] formEquationArr = TextUtility.splitString(formEquationListValue, "$$");
        for(int k=0; k<formEquationArr.length; k++){
          newStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE+k);
          newStepStep.setParentStepID(step.getID());
          newStepStep.setGroupID(new Integer(++groupCount));
          if((WorkflowStep.PARENT_TYPE_SINGLE+k).equals(step.getFollowBranch())){
            newStepStep.setChildStepID(oldChildStepID);
            //newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
            newStepStep.setLoopBack(oldLoopBack);//TC.ADD
          } else {
            newStepStep.setChildStepID(endStepID);
            newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_YES);
          }
          //            newStepStep.setChildStepID(oldChildStepID);
          //            newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
          stepStepDAO.insertObject(newStepStep);
        }
        
        //Decision Point Task
      } else if (WorkflowStep.ACTION_TYPE_USER_DEFINED_TASK.equals(step.getActionType())) {
        for(int k=0; k<routineArr.length; k++){
          newStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE+k);
          newStepStep.setParentStepID(step.getID());
          newStepStep.setGroupID(new Integer(++groupCount));
          if((WorkflowStep.PARENT_TYPE_SINGLE+k).equals(step.getFollowBranch())){
            newStepStep.setChildStepID(oldChildStepID);
            //newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
            newStepStep.setLoopBack(oldLoopBack);//TC.ADD
          } else {
            newStepStep.setChildStepID(endStepID);
            newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_YES);
          }
          stepStepDAO.insertObject(newStepStep);
        }
      } else {
        //        if (!WorkflowStep.ACTION_TYPE_APPROVAL.equals(step.getActionType()) &&
        //            !WorkflowStep.ACTION_TYPE_BOOLEAN.equals(step.getActionType()) &&
        //            !WorkflowStep.ACTION_TYPE_SYSTEM_AUTO.equals(step.getActionType()) &&
        //            !WorkflowStep.ACTION_TYPE_FORM_DRIVEN.equals(step.getActionType()) &&
        //            !WorkflowStep.ACTION_TYPE_USER_DEFINED_TASK.equals(step.getActionType()) ) {
        newStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE);
        newStepStep.setParentStepID(step.getID());
        newStepStep.setChildStepID(oldChildStepID);
        newStepStep.setGroupID(new Integer(++groupCount));
        //newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
        newStepStep.setLoopBack(oldLoopBack);
        stepStepDAO.insertObject(newStepStep);
      }
      
      // stepStepDAO.deleteUnusedObject(oldStepStep);
    } else if(!betweenStep) {
      MtmWorkflowStepWorkflowStep newStepStep = new MtmWorkflowStepWorkflowStep();
      //Auto task
      if (WorkflowStep.ACTION_TYPE_SYSTEM_AUTO.equals(step.getActionType()) ) {
        WorkflowAction tmpAction = (WorkflowAction) actionDAO.getObjectByID(step.getActionID());
        WorkflowProcessor processor = AutomaticTaskFactory.getInstance(tmpAction);
        int totalRoutine = processor.getNumOfRoutine();
        
        if (totalRoutine>1) {
          for (int j=0; j<totalRoutine; j++) {
            newStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE + j);
            newStepStep.setParentStepID(step.getID());
            newStepStep.setChildStepID(endStepID);
            newStepStep.setGroupID(new Integer(++groupCount));
            newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
            stepStepDAO.insertObject(newStepStep);
          }
        } else {
          newStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE);
          newStepStep.setParentStepID(step.getID());
          newStepStep.setChildStepID(endStepID);
          newStepStep.setGroupID(new Integer(++groupCount));
          newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
          stepStepDAO.insertObject(newStepStep);
        }
      } else if (WorkflowStep.ACTION_TYPE_APPROVAL.equals(step.getActionType())) {
        newStepStep.setParentType(WorkflowStep.PARENT_TYPE_APPROVED);
        newStepStep.setParentStepID(step.getID());
        newStepStep.setGroupID(new Integer(++groupCount));
        newStepStep.setChildStepID(endStepID);
        //newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);//simon_del
        newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_YES);//simon_add
        
        newStepStep = (MtmWorkflowStepWorkflowStep)stepStepDAO.insertObject(newStepStep);
        // MtmWorkflowStepWorkflowStep endStepStep = new MtmWorkflowStepWorkflowStep();
        
        newStepStep = new MtmWorkflowStepWorkflowStep();
        newStepStep.setParentType(WorkflowStep.PARENT_TYPE_REJECTED);
        newStepStep.setParentStepID(step.getID());
        newStepStep.setGroupID(new Integer(++groupCount));
        newStepStep.setChildStepID(endStepID);
        //newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);//simon_del
        newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_YES);//simon_add
        
        stepStepDAO.insertObject(newStepStep);
        //Yes / No Task
      } else if (WorkflowStep.ACTION_TYPE_BOOLEAN.equals(step.getActionType())) {
        newStepStep.setParentType(WorkflowStep.PARENT_TYPE_YES);
        newStepStep.setParentStepID(step.getID());
        newStepStep.setGroupID(new Integer(++groupCount));
        newStepStep.setChildStepID(endStepID);
        newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
        
        stepStepDAO.insertObject(newStepStep);
        
        newStepStep = new MtmWorkflowStepWorkflowStep();
        newStepStep.setParentType(WorkflowStep.PARENT_TYPE_NO);
        newStepStep.setParentStepID(step.getID());
        newStepStep.setGroupID(new Integer(++groupCount));
        newStepStep.setChildStepID(endStepID);
        newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
        
        stepStepDAO.insertObject(newStepStep);
        //Form Value Driven Task
      } else if (WorkflowStep.ACTION_TYPE_FORM_DRIVEN.equals(step.getActionType())) {
        String formEquationListValue = step.getFormEquationListValue();
        String[] formEquationArr = TextUtility.splitString(formEquationListValue, "$$");
        for(int k=0; k<formEquationArr.length; k++){
          newStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE+k);
          newStepStep.setParentStepID(step.getID());
          newStepStep.setGroupID(new Integer(++groupCount));
          newStepStep.setChildStepID(endStepID);
          newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
          //            newStepStep.setChildStepID(oldChildStepID);
          //            newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
          stepStepDAO.insertObject(newStepStep);
        }
        
        //Decision Point Task
      } else if (WorkflowStep.ACTION_TYPE_USER_DEFINED_TASK.equals(step.getActionType())) {
        for(int k=0; k<routineArr.length; k++){
          newStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE+k);
          newStepStep.setParentStepID(step.getID());
          newStepStep.setGroupID(new Integer(++groupCount));
          newStepStep.setChildStepID(endStepID);
          newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
          
          stepStepDAO.insertObject(newStepStep);
        }
      } else {
        //        if (!WorkflowStep.ACTION_TYPE_APPROVAL.equals(step.getActionType()) &&
        //            !WorkflowStep.ACTION_TYPE_BOOLEAN.equals(step.getActionType()) &&
        //            !WorkflowStep.ACTION_TYPE_SYSTEM_AUTO.equals(step.getActionType()) &&
        //            !WorkflowStep.ACTION_TYPE_FORM_DRIVEN.equals(step.getActionType()) &&
        //            !WorkflowStep.ACTION_TYPE_USER_DEFINED_TASK.equals(step.getActionType()) ) {
        newStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE);
        newStepStep.setParentStepID(step.getID());
        newStepStep.setChildStepID(endStepID);
        newStepStep.setGroupID(new Integer(++groupCount));
        newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
        stepStepDAO.insertObject(newStepStep);
      }
    }
    
    
    */
    step=createWorkflowStepBasicInfo(step);
    step=createWorkflowStepUserInfo(step);
    step=createWorkflowStepStepRelationShip(step,betweenStep);
    return  step;
  }
  /**
   * create new workflowStepBasicInformation
   * @param step
   * @return
   * @throws ApplicationException
   */
  private WorkflowStep createWorkflowStepBasicInfo(WorkflowStep step) throws ApplicationException{
    WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(sessionContainer, dbConn);
    step = setWorkflowStepBasicInfo(step);
    step = (WorkflowStep) stepDAO.insertObject(step);
    return step;
  }
  /**
   * set workflowStep basic Information
   * about: actionType,actionID,Complete ActionStr ,OverDue ActionStr
   * @param step
   * @return
   */
  private WorkflowStep setWorkflowStepBasicInfo(WorkflowStep step){
    String actionType = step.getActionType();
    
    //ADD THE ACTIONTYPE
    if (actionType!=null && actionType.length() > 1) {
      String sActionID = actionType.substring(1);
      step.setActionType(actionType.substring(0, 1));
      step.setActionID(TextUtility.parseIntegerObj(sActionID));
    }
    
    StringBuffer sNotifyStr = new StringBuffer();
    StringBuffer sOverdueStr = new StringBuffer();
    //Notify INFO
    if (step.isNotifyOwnerTaskArrivalByMail()) {
      sNotifyStr.append(step.getNotifyOwnerTaskArrivalByMail());
    }
    if (step.isNotifyOwnerTaskArrivalBySystem()) {
      sNotifyStr.append(step.getNotifyOwnerTaskArrivalBySystem());
    }
    if (step.isNotifyAllTaskCompletedByMail()) {
      sNotifyStr.append(step.getNotifyAllTaskCompletedByMail());
    }
    if (step.isNotifyAllTaskCompletedBySystem()) {
      sNotifyStr.append(step.getNotifyAllTaskCompletedBySystem());
    }
    step.setOnCompleteAckMethod(sNotifyStr.toString());
    
    //Overdue INFO
    if (step.isNotifyOwnerTaskOverdueByMail()) {
      sOverdueStr.append(step.getNotifyOwnerTaskOverdueByMail());
    }
    if (step.isNotifyOwnerTaskOverdueBySystem()) {
      sOverdueStr.append(step.getNotifyOwnerTaskOverdueBySystem());
    }
    if (step.isNotifyAllTaskOverdueByMail()) {
      sOverdueStr.append(step.getNotifyAllTaskOverdueByMail());
    }
    if (step.isNotifyAllTaskOverdueBySystem()) {
      sOverdueStr.append(step.getNotifyAllTaskOverdueBySystem());
    }
    step.setOnNoResponseAckMethod(sOverdueStr.toString()); 
    return step;
  }
  /**
   * create MtmWorkflowStepUser Infomation
   * @param step
   * @return
   * @throws ApplicationException
   */
  private WorkflowStep createWorkflowStepUserInfo(WorkflowStep step) throws ApplicationException{
    MtmWorkflowStepUserActorDAObject stepActorDAO = new MtmWorkflowStepUserActorDAObject(sessionContainer, dbConn);
    //create user actor for the step
    String permData = TextUtility.replaceString(step.getAllPermissionData(), "\r", "");
    String[] tmpAry = TextUtility.splitString(permData, "\n");
    if (tmpAry == null) {
      tmpAry = new String[0];
    }
    //* specific user account
    for (int i = 0; i < tmpAry.length; i++) {
      if (!Utility.isEmpty(tmpAry[i])) {
        String[] rowAry = TextUtility.splitString(tmpAry[i], "\t");
        MtmWorkflowStepUserActor mtmWorkflowStepUserActor = new MtmWorkflowStepUserActor();
        mtmWorkflowStepUserActor.setWorkflowStepID(step.getID());
        if (MtmWorkflowStepUserActor.ACTOR_TYPE_GROUP.equals(rowAry[2]) || MtmWorkflowStepUserActor.ACTOR_TYPE_ROLE.equals(rowAry[2]) || MtmWorkflowStepUserActor.ACTOR_TYPE_USER.equals(rowAry[2])){
          mtmWorkflowStepUserActor.setActorType(rowAry[2]);
          mtmWorkflowStepUserActor.setActorID(TextUtility.parseIntegerObj(rowAry[0]));
        } else {
          mtmWorkflowStepUserActor.setActorType(rowAry[0].substring(0,1));
          if(MtmWorkflowStepUserActor.ACTOR_TYPE_ACTION_TAKER.equals(rowAry[0].substring(0,1)) ||
              MtmWorkflowStepUserActor.ACTOR_TYPE_REPORT_TO_OF_ACTION_TAKER.equals(rowAry[0].substring(0,1))){
            mtmWorkflowStepUserActor.setActorID( new Integer(rowAry[0].substring(1)));
          }else{
            mtmWorkflowStepUserActor.setActorID(new Integer(0));
          }
        }
        mtmWorkflowStepUserActor.setPermission(rowAry[3]);
        mtmWorkflowStepUserActor.setActionType(MtmWorkflowStepUserActor.ACTION_TYPE_EXECUTION);
        mtmWorkflowStepUserActor.setRecordStatus(GlobalConstant.RECORD_STATUS_ACTIVE);     // falcon_add
        stepActorDAO.insertObject(mtmWorkflowStepUserActor);
      }
    }
    
    // add the assign at runtime actor type
    String assignActorType = step.getAssignActorType();
    if (!Utility.isEmpty(assignActorType)) {
      MtmWorkflowStepUserActor mtmWorkflowStepUserActor = new MtmWorkflowStepUserActor();
      mtmWorkflowStepUserActor.setWorkflowStepID(step.getID());
      mtmWorkflowStepUserActor.setActorType(assignActorType);
      mtmWorkflowStepUserActor.setActorID(new Integer(0));
      mtmWorkflowStepUserActor.setActionType(MtmWorkflowStepUserActor.ACTION_TYPE_EXECUTION);
      stepActorDAO.insertObject(mtmWorkflowStepUserActor);
    }
    return step;
  }
  /**
   * create MtmWorkflowStepWorkflowStep relation Ship
   * @param step,betweenStep
   * @return
   * @throws ApplicationException
   */
  private WorkflowStep createWorkflowStepStepRelationShip(WorkflowStep step, boolean betweenStep) throws ApplicationException{
    //* create workflow steps relation
    MtmWorkflowStepWorkflowStepDAObject stepStepDAO = new MtmWorkflowStepWorkflowStepDAObject(sessionContainer, dbConn);
    WorkflowActionDAObject actionDAO = new WorkflowActionDAObject(sessionContainer, dbConn);
    WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(sessionContainer, dbConn);
    
    WorkflowStep endStep = (WorkflowStep)stepDAO.getEndStepByWorkflowRecordID(step.getWorkflowRecordID());
    
    //get the end step ID
    Integer endStepID = endStep.getID();
    
    //get the routineListValue
    String routeListValue = step.getRouteListValue();
    String[] routineArr = null;
    if(!Utility.isEmpty(routeListValue) && !"null".equals(routeListValue)){
      routineArr = TextUtility.splitString(routeListValue,"|");
    }
    
    //* find group id
    log.debug("add workflow step workflow step record ");
    Integer nextGroupID = stepStepDAO.getNextGroupIDForWorkflowRecord(step.getWorkflowRecordID());
    if (nextGroupID == null) {
      nextGroupID = new Integer(0);
    }
    int groupCount = nextGroupID.intValue();
    String[] parentStepAry = step.getParentStepIDs();
    MtmWorkflowStepWorkflowStep oldStepStep = null;
    String parentType = null;
    String parentStepID = null;
    Integer oldChildStepID = null;
    String oldLoopBack=null;//TC ADD FOR:if have old parent-child ship,currentStep-Child ship = old parent-child ship
    for (int i = 0; i < parentStepAry.length; i++) {
      //MtmWorkflowStepWorkflowStep mtmWorkflowStepWorkflowStep = new MtmWorkflowStepWorkflowStep();
      parentType = parentStepAry[i].substring(0,parentStepAry[i].indexOf("-"));
      parentStepID = parentStepAry[i].substring(parentStepAry[i].indexOf("-")+1);
      log.debug("parent type: "+parentType);
      log.debug("parent step id: "+parentStepID);
      if(betweenStep){
        oldStepStep = (MtmWorkflowStepWorkflowStep) stepStepDAO.getParentStepToChildStepRecord(step.getWorkflowRecordID(), TextUtility.parseIntegerObj(parentStepID),parentType, step.getCurrentStepID());
      } else {
        oldStepStep = (MtmWorkflowStepWorkflowStep) stepStepDAO.getParentStepToEndStepRecord(step.getWorkflowRecordID(), parentType, TextUtility.parseIntegerObj(parentStepID));
      }
      
      log.debug("the frist oldchildStr is "+oldChildStepID );
      if (oldStepStep!= null) {
        oldChildStepID = oldStepStep.getChildStepID();
        oldLoopBack = oldStepStep.getLoopBack();//TC.ADD for currentStep-child ship next
        oldStepStep.setParentType(parentType);
        oldStepStep.setParentStepID(TextUtility.parseIntegerObj(parentStepID));
        oldStepStep.setChildStepID(step.getID());
        oldStepStep.setGroupID(nextGroupID);
        oldStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
        oldStepStep = (MtmWorkflowStepWorkflowStep) stepStepDAO.updateObject(oldStepStep);
      } else {
        MtmWorkflowStepWorkflowStep oldStepStepCreate = new MtmWorkflowStepWorkflowStep();
        oldStepStepCreate.setParentType(parentType);
        oldStepStepCreate.setParentStepID(TextUtility.parseIntegerObj(parentStepID));
        oldStepStepCreate.setChildStepID(step.getID());
        oldStepStepCreate.setGroupID(nextGroupID);
        oldStepStepCreate.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
        oldStepStepCreate = (MtmWorkflowStepWorkflowStep) stepStepDAO.insertObject(oldStepStepCreate);
      }
    }
    log.debug("the frist oldchildStr is "+oldChildStepID );
    //get oldStepStep by steptype
    
    if (oldStepStep!=null) {
      MtmWorkflowStepWorkflowStep newStepStep = new MtmWorkflowStepWorkflowStep();
      //Auto task
      if (WorkflowStep.ACTION_TYPE_SYSTEM_AUTO.equals(step.getActionType()) ) {
        WorkflowAction tmpAction = (WorkflowAction) actionDAO.getObjectByID(step.getActionID());
        WorkflowProcessor processor = AutomaticTaskFactory.getInstance(tmpAction);//Edit by TC for AutomaticTask HotDeploy
        int totalRoutine = processor.getNumOfRoutine();
        
        if (totalRoutine>1) {
          for (int j=0; j<totalRoutine; j++) {
            newStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE + j);
            newStepStep.setParentStepID(step.getID());
            newStepStep.setChildStepID(oldChildStepID);
            newStepStep.setGroupID(new Integer(++groupCount));
            //newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
            //WHY = LOOP_BACK_NO? THERE IS NO REASON ##TC.ADD START
            if(j==0) {
              newStepStep.setLoopBack(oldLoopBack);
            } else {
              newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
            }
            //TC.ADD END
            stepStepDAO.insertObject(newStepStep);
          }
        } else {
          newStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE);
          newStepStep.setParentStepID(step.getID());
          newStepStep.setChildStepID(oldChildStepID);
          newStepStep.setGroupID(new Integer(++groupCount));
          newStepStep.setLoopBack(oldLoopBack);//TC.ADD
          stepStepDAO.insertObject(newStepStep);
        }
      } else if (WorkflowStep.ACTION_TYPE_APPROVAL.equals(step.getActionType())) {
        newStepStep.setParentType(WorkflowStep.PARENT_TYPE_APPROVED);
        newStepStep.setParentStepID(step.getID());
        newStepStep.setGroupID(new Integer(++groupCount));
        if(WorkflowStep.PARENT_TYPE_APPROVED.equals(step.getFollowBranch())){
          newStepStep.setChildStepID(oldChildStepID);
          newStepStep.setLoopBack(oldLoopBack);//TC.ADD
        } else {
          newStepStep.setChildStepID(endStepID);
          newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_YES);
        }
        stepStepDAO.insertObject(newStepStep);
        
        newStepStep = new MtmWorkflowStepWorkflowStep();
        newStepStep.setParentType(WorkflowStep.PARENT_TYPE_REJECTED);
        newStepStep.setParentStepID(step.getID());
        newStepStep.setGroupID(new Integer(++groupCount));
        if(WorkflowStep.PARENT_TYPE_REJECTED.equals(step.getFollowBranch())){
          newStepStep.setChildStepID(oldChildStepID);
          newStepStep.setLoopBack(oldLoopBack);
        } else {
          newStepStep.setChildStepID(endStepID);
          newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_YES);
        }
        stepStepDAO.insertObject(newStepStep);
        //Yes / No Task
      } else if (WorkflowStep.ACTION_TYPE_BOOLEAN.equals(step.getActionType())) {
        newStepStep.setParentType(WorkflowStep.PARENT_TYPE_YES);
        newStepStep.setParentStepID(step.getID());
        newStepStep.setGroupID(new Integer(++groupCount));
        if(WorkflowStep.PARENT_TYPE_YES.equals(step.getFollowBranch())){
          newStepStep.setChildStepID(oldChildStepID);
          newStepStep.setLoopBack(oldLoopBack);//TC.ADD
        } else {
          newStepStep.setChildStepID(endStepID);
          newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_YES);
        }
        stepStepDAO.insertObject(newStepStep);
        
        newStepStep = new MtmWorkflowStepWorkflowStep();
        newStepStep.setParentType(WorkflowStep.PARENT_TYPE_NO);
        newStepStep.setParentStepID(step.getID());
        newStepStep.setGroupID(new Integer(++groupCount));
        if(WorkflowStep.PARENT_TYPE_NO.equals(step.getFollowBranch())){
          newStepStep.setChildStepID(oldChildStepID);
          newStepStep.setLoopBack(oldLoopBack);//TC.ADD
        } else {
          newStepStep.setChildStepID(endStepID);
          newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_YES);
        }
        stepStepDAO.insertObject(newStepStep);
        //Form Value Driven Task
      } else if (WorkflowStep.ACTION_TYPE_FORM_DRIVEN.equals(step.getActionType())) {
        String formEquationListValue = step.getFormEquationListValue();
        String[] formEquationArr = TextUtility.splitString(formEquationListValue, "$$");
        for(int k=0; k<formEquationArr.length; k++){
          newStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE+k);
          newStepStep.setParentStepID(step.getID());
          newStepStep.setGroupID(new Integer(++groupCount));
          if((WorkflowStep.PARENT_TYPE_SINGLE+k).equals(step.getFollowBranch())){
            newStepStep.setChildStepID(oldChildStepID);
            newStepStep.setLoopBack(oldLoopBack);//TC.ADD
          } else {
            newStepStep.setChildStepID(endStepID);
            newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_YES);
          }
          stepStepDAO.insertObject(newStepStep);
        }
        
        //Decision Point Task
      } else if (WorkflowStep.ACTION_TYPE_USER_DEFINED_TASK.equals(step.getActionType())) {
        for(int k=0; k<routineArr.length; k++){
          newStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE+k);
          newStepStep.setParentStepID(step.getID());
          newStepStep.setGroupID(new Integer(++groupCount));
          if((WorkflowStep.PARENT_TYPE_SINGLE+k).equals(step.getFollowBranch())){
            newStepStep.setChildStepID(oldChildStepID);
            newStepStep.setLoopBack(oldLoopBack);//TC.ADD
          } else {
            newStepStep.setChildStepID(endStepID);
            newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_YES);
          }
          stepStepDAO.insertObject(newStepStep);
        }
      } else {
        newStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE);
        newStepStep.setParentStepID(step.getID());
        newStepStep.setChildStepID(oldChildStepID);
        newStepStep.setGroupID(new Integer(++groupCount));
        newStepStep.setLoopBack(oldLoopBack);
        stepStepDAO.insertObject(newStepStep);
      }
      
      // stepStepDAO.deleteUnusedObject(oldStepStep);
    } else if(!betweenStep) {
      MtmWorkflowStepWorkflowStep newStepStep = new MtmWorkflowStepWorkflowStep();
      //Auto task
      if (WorkflowStep.ACTION_TYPE_SYSTEM_AUTO.equals(step.getActionType()) ) {
        WorkflowAction tmpAction = (WorkflowAction) actionDAO.getObjectByID(step.getActionID());
        WorkflowProcessor processor = AutomaticTaskFactory.getInstance(tmpAction);
        int totalRoutine = processor.getNumOfRoutine();
        
        if (totalRoutine>1) {
          for (int j=0; j<totalRoutine; j++) {
            newStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE + j);
            newStepStep.setParentStepID(step.getID());
            newStepStep.setChildStepID(endStepID);
            newStepStep.setGroupID(new Integer(++groupCount));
            newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
            stepStepDAO.insertObject(newStepStep);
          }
        } else {
          newStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE);
          newStepStep.setParentStepID(step.getID());
          newStepStep.setChildStepID(endStepID);
          newStepStep.setGroupID(new Integer(++groupCount));
          newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
          stepStepDAO.insertObject(newStepStep);
        }
      } else if (WorkflowStep.ACTION_TYPE_APPROVAL.equals(step.getActionType())) {
        newStepStep.setParentType(WorkflowStep.PARENT_TYPE_APPROVED);
        newStepStep.setParentStepID(step.getID());
        newStepStep.setGroupID(new Integer(++groupCount));
        newStepStep.setChildStepID(endStepID);
        //newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);//simon_del
        newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_YES);//simon_add
        
        newStepStep = (MtmWorkflowStepWorkflowStep)stepStepDAO.insertObject(newStepStep);
        // MtmWorkflowStepWorkflowStep endStepStep = new MtmWorkflowStepWorkflowStep();
        
        newStepStep = new MtmWorkflowStepWorkflowStep();
        newStepStep.setParentType(WorkflowStep.PARENT_TYPE_REJECTED);
        newStepStep.setParentStepID(step.getID());
        newStepStep.setGroupID(new Integer(++groupCount));
        newStepStep.setChildStepID(endStepID);
        //newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);//simon_del
        newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_YES);//simon_add
        
        stepStepDAO.insertObject(newStepStep);
        //Yes / No Task
      } else if (WorkflowStep.ACTION_TYPE_BOOLEAN.equals(step.getActionType())) {
        newStepStep.setParentType(WorkflowStep.PARENT_TYPE_YES);
        newStepStep.setParentStepID(step.getID());
        newStepStep.setGroupID(new Integer(++groupCount));
        newStepStep.setChildStepID(endStepID);
        newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
        
        stepStepDAO.insertObject(newStepStep);
        
        newStepStep = new MtmWorkflowStepWorkflowStep();
        newStepStep.setParentType(WorkflowStep.PARENT_TYPE_NO);
        newStepStep.setParentStepID(step.getID());
        newStepStep.setGroupID(new Integer(++groupCount));
        newStepStep.setChildStepID(endStepID);
        newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
        
        stepStepDAO.insertObject(newStepStep);
        //Form Value Driven Task
      } else if (WorkflowStep.ACTION_TYPE_FORM_DRIVEN.equals(step.getActionType())) {
        String formEquationListValue = step.getFormEquationListValue();
        String[] formEquationArr = TextUtility.splitString(formEquationListValue, "$$");
        for(int k=0; k<formEquationArr.length; k++){
          newStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE+k);
          newStepStep.setParentStepID(step.getID());
          newStepStep.setGroupID(new Integer(++groupCount));
          newStepStep.setChildStepID(endStepID);
          newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
          //            newStepStep.setChildStepID(oldChildStepID);
          //            newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
          stepStepDAO.insertObject(newStepStep);
        }
        
        //Decision Point Task
      } else if (WorkflowStep.ACTION_TYPE_USER_DEFINED_TASK.equals(step.getActionType())) {
        for(int k=0; k<routineArr.length; k++){
          newStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE+k);
          newStepStep.setParentStepID(step.getID());
          newStepStep.setGroupID(new Integer(++groupCount));
          newStepStep.setChildStepID(endStepID);
          newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
          
          stepStepDAO.insertObject(newStepStep);
        }
      } else {
        //        if (!WorkflowStep.ACTION_TYPE_APPROVAL.equals(step.getActionType()) &&
        //            !WorkflowStep.ACTION_TYPE_BOOLEAN.equals(step.getActionType()) &&
        //            !WorkflowStep.ACTION_TYPE_SYSTEM_AUTO.equals(step.getActionType()) &&
        //            !WorkflowStep.ACTION_TYPE_FORM_DRIVEN.equals(step.getActionType()) &&
        //            !WorkflowStep.ACTION_TYPE_USER_DEFINED_TASK.equals(step.getActionType()) ) {
        newStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE);
        newStepStep.setParentStepID(step.getID());
        newStepStep.setChildStepID(endStepID);
        newStepStep.setGroupID(new Integer(++groupCount));
        newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
        stepStepDAO.insertObject(newStepStep);
      }
    }
    return step;
  }
  /**
   * Updates a given WorkflowStep object. Note that you should set ALL the current step
   * property, or otherwise the system will set to default when update. The rule-based
   * notifications and priority escalation will not be updated here, you may need to call
   * the according Data Access Object's update method explicitly.
   *
   * @param oldStep and step The step to be updated
   * @return void
   * @throws ApplicationException
   * @see com.dcivision.bean.MtmWorkflowStepUserActor
   */
  public void updateWorkflowStepActionType(WorkflowStep oldStep,WorkflowStep step) throws ApplicationException {
    log.info("-=-=-=-=-=-=-updateWorkflowStepActionType()-=-=-=-=-=-=-");
    MtmWorkflowStepWorkflowStepDAObject stepStepDAO = new MtmWorkflowStepWorkflowStepDAObject(this.sessionContainer, this.dbConn);
    WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(this.sessionContainer, this.dbConn);
        
    boolean isOldApprovel = WorkflowStep.ACTION_TYPE_APPROVAL.equals(oldStep.getActionType());
    boolean isOldTODO = WorkflowStep.ACTION_TYPE_TASK_TO_DO.equals(oldStep.getActionType());
    boolean isOldBoolean = WorkflowStep.ACTION_TYPE_BOOLEAN.equals(oldStep.getActionType());
    boolean isNewApproval = WorkflowStep.ACTION_TYPE_APPROVAL.equals(step.getActionType());     
    boolean isNewToDo = WorkflowStep.ACTION_TYPE_TASK_TO_DO.equals(step.getActionType());
    boolean isNewBoolean = WorkflowStep.ACTION_TYPE_BOOLEAN.equals(step.getActionType());
    
    List childList = stepStepDAO.getListByParentStepID(step.getID());
    
    if (isOldTODO && isNewApproval) {
      WorkflowStep endStep = (WorkflowStep)stepDAO.getEndStepByWorkflowRecordID(step.getWorkflowRecordID());
      for (int i=0; i<childList.size(); i++) {
        MtmWorkflowStepWorkflowStep childStepStep = (MtmWorkflowStepWorkflowStep) childList.get(i); 
        stepStepDAO.deleteObject(childStepStep);  
      }
      for(int i=0;i<2;i++){
        int groupCount = stepStepDAO.getMaxGroupIDByWorkflowRecordID(step.getWorkflowRecordID()).intValue();
        
        MtmWorkflowStepWorkflowStep newStepStep = new MtmWorkflowStepWorkflowStep();
        if(i==0){
          newStepStep.setParentType(WorkflowStep.PARENT_TYPE_APPROVED);
        }else{
          newStepStep.setParentType(WorkflowStep.PARENT_TYPE_REJECTED); 
        }          
        newStepStep.setParentStepID(step.getID());
        newStepStep.setGroupID(new Integer(++groupCount));
        //newStepStep.setChildStepID(endStep.getID());
        //newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);//TC.DEL
        //-TC.ADD START-
        if(newStepStep.getParentType().equals(step.getFollowBranch())){
          newStepStep.setChildStepID(((MtmWorkflowStepWorkflowStep)childList.get(0)).getChildStepID());
          newStepStep.setLoopBack(((MtmWorkflowStepWorkflowStep)childList.get(0)).getLoopBack());
        }else{
          newStepStep.setChildStepID(endStep.getID());
          newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_YES);
        }
        //-TC.ADD END
        stepStepDAO.insertObject(newStepStep);
      }
    }else if (isOldApprovel && isNewToDo) {
      //there need modify: should have reserveStepID to decide which branch to be reserved
      for (int i=0; i<childList.size(); i++) {
        MtmWorkflowStepWorkflowStep childStepStep = (MtmWorkflowStepWorkflowStep) childList.get(i);
        if(Utility.isEmpty(step.getFollowBranch())){
          if(WorkflowStep.PARENT_TYPE_APPROVED.equals(childStepStep.getParentType())){
            childStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE);
            stepStepDAO.updateObject(childStepStep);  
          }else if(WorkflowStep.PARENT_TYPE_REJECTED.equals(childStepStep.getParentType())){
            stepStepDAO.deleteObject(childStepStep);
          }
        }else{////TC.ADD FOR THE CONDITION : exists followBranch that specify which branch will reserved
          if(childStepStep.getParentType().equals(step.getFollowBranch())){
            childStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE);
            stepStepDAO.updateObject(childStepStep);  
          }else{
            stepStepDAO.deleteObject(childStepStep);
          }
        }
      }
    }else if (isOldTODO&&isNewBoolean) {
      WorkflowStep endStep = (WorkflowStep)stepDAO.getEndStepByWorkflowRecordID(step.getWorkflowRecordID());
      for (int i=0; i<childList.size(); i++) {
        MtmWorkflowStepWorkflowStep childStepStep = (MtmWorkflowStepWorkflowStep) childList.get(i); 
        stepStepDAO.deleteObject(childStepStep);  
      }
      for(int i=0;i<2;i++){
        int groupCount = stepStepDAO.getMaxGroupIDByWorkflowRecordID(step.getWorkflowRecordID()).intValue();
        
        MtmWorkflowStepWorkflowStep newStepStep = new MtmWorkflowStepWorkflowStep();
        if(i==0){
          newStepStep.setParentType(WorkflowStep.PARENT_TYPE_YES);
        }else{
          newStepStep.setParentType(WorkflowStep.PARENT_TYPE_NO); 
        }          
        newStepStep.setParentStepID(step.getID());
        newStepStep.setGroupID(new Integer(++groupCount));
        //newStepStep.setChildStepID(endStep.getID());
        //newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
        //-TC.ADD START-
        if(newStepStep.getParentType().equals(step.getFollowBranch())){
          newStepStep.setChildStepID(((MtmWorkflowStepWorkflowStep)childList.get(0)).getChildStepID());
          newStepStep.setLoopBack(((MtmWorkflowStepWorkflowStep)childList.get(0)).getLoopBack());
        }else{
          newStepStep.setChildStepID(endStep.getID());
          newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_YES);
        }
        //-TC.ADD END
        stepStepDAO.insertObject(newStepStep);
      }
    }else if (isOldBoolean&&isNewToDo) {
      for (int i=0; i<childList.size(); i++) {
        MtmWorkflowStepWorkflowStep childStepStep = (MtmWorkflowStepWorkflowStep) childList.get(i);
        if(Utility.isEmpty(step.getFollowBranch())){
          if(WorkflowStep.PARENT_TYPE_YES.equals(childStepStep.getParentType())){
            childStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE);
            stepStepDAO.updateObject(childStepStep);  
          }else if(WorkflowStep.PARENT_TYPE_NO.equals(childStepStep.getParentType())){
            stepStepDAO.deleteObject(childStepStep);
          }
        }else{//TC.ADD FOR THE CONDITION : exists followBranch that specify which branch will reserved
          if(childStepStep.getParentType().equals(step.getFollowBranch())){
            childStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE);
            stepStepDAO.updateObject(childStepStep);
          }else{
            stepStepDAO.deleteObject(childStepStep);
          }
        }
      }
    }else if(isOldApprovel&&isNewBoolean){
      for (int i=0; i<childList.size(); i++) {
        MtmWorkflowStepWorkflowStep childStepStep = (MtmWorkflowStepWorkflowStep) childList.get(i); 
        if(WorkflowStep.PARENT_TYPE_APPROVED.equals(childStepStep.getParentType())){
          childStepStep.setParentType(WorkflowStep.PARENT_TYPE_YES);
          stepStepDAO.updateObject(childStepStep);
        }else if(WorkflowStep.PARENT_TYPE_REJECTED.equals(childStepStep.getParentType())){
          childStepStep.setParentType(WorkflowStep.PARENT_TYPE_NO);
          stepStepDAO.updateObject(childStepStep);
        } 
      }
    }else if(isOldBoolean&&isNewApproval){
      for (int i=0; i<childList.size(); i++) {
        MtmWorkflowStepWorkflowStep childStepStep = (MtmWorkflowStepWorkflowStep) childList.get(i); 
        if(WorkflowStep.PARENT_TYPE_YES.equals(childStepStep.getParentType())){
          childStepStep.setParentType(WorkflowStep.PARENT_TYPE_APPROVED);
          stepStepDAO.updateObject(childStepStep);
        }else if(WorkflowStep.PARENT_TYPE_NO.equals(childStepStep.getParentType())){
          childStepStep.setParentType(WorkflowStep.PARENT_TYPE_REJECTED);
          stepStepDAO.updateObject(childStepStep);
        } 
      }
    }
  }  
  /**
   * Updates a given WorkflowStep object. Note that you should set ALL the current step
   * property, or otherwise the system will set to default when update. The rule-based
   * notifications and priority escalation will not be updated here, you may need to call
   * the according Data Access Object's update method explicitly.
   *
   * @param step The step to be updated
   * @return The updated WorkflowStep
   * @throws ApplicationException
   * @see com.dcivision.bean.MtmWorkflowStepUserActor
   */
  public WorkflowStep updateWorkflowStep(WorkflowStep step) throws ApplicationException {
    log.info("-=-=-=-=-=-=-Enter update workflow step-=-=-=-=-=-=-");
    /*MtmWorkflowStepWorkflowStepDAObject stepStepDAO = new MtmWorkflowStepWorkflowStepDAObject(this.sessionContainer, this.dbConn);
    WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(this.sessionContainer, this.dbConn);
    MtmWorkflowStepUserActorDAObject stepActorDAO = new MtmWorkflowStepUserActorDAObject(this.sessionContainer, this.dbConn);
    WorkflowActionDAObject actionDAO = new WorkflowActionDAObject(sessionContainer, dbConn);
    
    WorkflowStep oldStep = (WorkflowStep) stepDAO.getObjectByID(step.getID());
    
    String actionType = step.getActionType();
    if (actionType!=null && actionType.length() > 1) {
      String sActionID = actionType.substring(1);
      step.setActionType(actionType.substring(0, 1));
      step.setActionID(TextUtility.parseIntegerObj(sActionID));
    }
    
    StringBuffer sNotifyStr = new StringBuffer();
    StringBuffer sOverdueStr = new StringBuffer();
    if (step.isNotifyOwnerTaskArrivalByMail()) {
      sNotifyStr.append(step.getNotifyOwnerTaskArrivalByMail());
    }
    if (step.isNotifyOwnerTaskArrivalBySystem()) {
      sNotifyStr.append(step.getNotifyOwnerTaskArrivalBySystem());
    }
    if (step.isNotifyAllTaskCompletedByMail()) {
      sNotifyStr.append(step.getNotifyAllTaskCompletedByMail());
    }
    if (step.isNotifyAllTaskCompletedBySystem()) {
      sNotifyStr.append(step.getNotifyAllTaskCompletedBySystem());
    }
    step.setOnCompleteAckMethod(sNotifyStr.toString());
    
    if (step.isNotifyOwnerTaskOverdueByMail()) {
      sOverdueStr.append(step.getNotifyOwnerTaskOverdueByMail());
    }
    if (step.isNotifyOwnerTaskOverdueBySystem()) {
      sOverdueStr.append(step.getNotifyOwnerTaskOverdueBySystem());
    }
    if (step.isNotifyAllTaskOverdueByMail()) {
      sOverdueStr.append(step.getNotifyAllTaskOverdueByMail());
    }
    if (step.isNotifyAllTaskOverdueBySystem()) {
      sOverdueStr.append(step.getNotifyAllTaskOverdueBySystem());
    }
    step.setOnNoResponseAckMethod(sOverdueStr.toString());
    
    //check the filter by
    
    
    step = (WorkflowStep) stepDAO.updateObject(step);
    
       
    // delete old MtmWorkflowStepUserActor record
    stepActorDAO.deleteListByWorkflowStepID(step.getID());
    //  create user actor for the step
    String permData = TextUtility.replaceString(step.getAllPermissionData(), "\r", "");
    String[] tmpAry = TextUtility.splitString(permData, "\n");
    if (tmpAry == null) {
      tmpAry = new String[0];
    }
    //* specific user account
    for (int i = 0; i < tmpAry.length; i++) {
      if (!Utility.isEmpty(tmpAry[i])) {
        String[] rowAry = TextUtility.splitString(tmpAry[i], "\t");
        MtmWorkflowStepUserActor mtmWorkflowStepUserActor = new MtmWorkflowStepUserActor();
        mtmWorkflowStepUserActor.setWorkflowStepID(step.getID());
        if (MtmWorkflowStepUserActor.ACTOR_TYPE_GROUP.equals(rowAry[2]) || MtmWorkflowStepUserActor.ACTOR_TYPE_ROLE.equals(rowAry[2]) || MtmWorkflowStepUserActor.ACTOR_TYPE_USER.equals(rowAry[2])){
          mtmWorkflowStepUserActor.setActorType(rowAry[2]);
          mtmWorkflowStepUserActor.setActorID(TextUtility.parseIntegerObj(rowAry[0]));
        } else {
          mtmWorkflowStepUserActor.setActorType(rowAry[0].substring(0,1));
          if(MtmWorkflowStepUserActor.ACTOR_TYPE_ACTION_TAKER.equals(rowAry[0].substring(0,1)) ||
              MtmWorkflowStepUserActor.ACTOR_TYPE_REPORT_TO_OF_ACTION_TAKER.equals(rowAry[0].substring(0,1))){
            mtmWorkflowStepUserActor.setActorID(new Integer(rowAry[0].substring(1)));
          }else{
            mtmWorkflowStepUserActor.setActorID(new Integer(0));
          }
        }
        mtmWorkflowStepUserActor.setPermission(rowAry[3]);
        mtmWorkflowStepUserActor.setActionType(MtmWorkflowStepUserActor.ACTION_TYPE_EXECUTION);
        if(MtmWorkflowStepUserActor.ACTOR_TYPE_RUNTIME_ASSIGN.equals(step.getAssignActorType())){
         // mtmWorkflowStepUserActor.setRecordStatus(GlobalConstant.RECORD_STATUS_PENDING);  // falcon_delete
        	mtmWorkflowStepUserActor.setRecordStatus(GlobalConstant.RECORD_STATUS_ACTIVE);   // falcon_add
        }
        stepActorDAO.insertObject(mtmWorkflowStepUserActor);
      }
    }
    
    // add the assign at runtime actor type
    String assignActorType = step.getAssignActorType();
    if (!Utility.isEmpty(assignActorType)) {
      MtmWorkflowStepUserActor mtmWorkflowStepUserActor = new MtmWorkflowStepUserActor();
      mtmWorkflowStepUserActor.setWorkflowStepID(step.getID());
      mtmWorkflowStepUserActor.setActorType(assignActorType);
      mtmWorkflowStepUserActor.setActorID(new Integer(0));
      mtmWorkflowStepUserActor.setActionType(MtmWorkflowStepUserActor.ACTION_TYPE_EXECUTION);
      stepActorDAO.insertObject(mtmWorkflowStepUserActor);
    }
     
    //  get the routineListValue
    String routeListValue = step.getRouteListValue();
    String[] routineArr = null;
    if(!Utility.isEmpty(routeListValue) && !"null".equals(routeListValue)){
      routineArr = TextUtility.splitString(routeListValue,"|");
    }
    
    if(WorkflowStep.ACTION_TYPE_FORM_DRIVEN.equals(oldStep.getActionType()) ){
      if(!Utility.isEmpty(step.getFormEquationListValue()) && !"null".equals(step.getFormEquationListValue())){
        routineArr = TextUtility.splitString(step.getFormEquationListValue(),"$$");
      }
    }
    
    
    //* update workflow step workflow step relation
    List childList = stepStepDAO.getListByParentStepID(step.getID());
    if (WorkflowStep.ACTION_TYPE_USER_DEFINED_TASK.equals(oldStep.getActionType()) ||
        WorkflowStep.ACTION_TYPE_FORM_DRIVEN.equals(oldStep.getActionType()) ) {
      if( !Utility.isEmpty(routineArr) && routineArr.length < childList.size()  ) {
        //delete the remove rountine
        for(int j=routineArr.length; j<childList.size(); j++ ){
          MtmWorkflowStepWorkflowStep tmpStepStep = (MtmWorkflowStepWorkflowStep)childList.get(j);
          stepStepDAO.deleteObject(tmpStepStep);
        }
      } else if (!Utility.isEmpty(routineArr) && routineArr.length > childList.size() ){
        //get the end step id
        List stepList = stepDAO.getListByWorkflowRecordID(step.getWorkflowRecordID());
        Integer endStepID = null;
        for(int i=0; i<stepList.size(); i++){
          WorkflowStep tmpStep = (WorkflowStep)stepList.get(i);
          if(WorkflowStep.END_STEP_SEQ_NO.equals(tmpStep.getStepSeq())){
            endStepID = tmpStep.getID();
          }
        }
        
        //insert the new MtmWorkflowStepWorkflowStep.
        //MtmWorkflowStepWorkflowStep lastStepStep = (MtmWorkflowStepWorkflowStep)childList.get(childList.size()-1);
        //int groupCount = lastStepStep.getGroupID().intValue();
        // get the max group ID from Mtm_WF_Step_WF_Step table
        int groupCount = stepStepDAO.getMaxGroupIDByWorkflowRecordID(step.getWorkflowRecordID()).intValue();
        
        for(int k=childList.size(); k<routineArr.length; k++){
          MtmWorkflowStepWorkflowStep newStepStep = new MtmWorkflowStepWorkflowStep();
          newStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE+k);
          newStepStep.setParentStepID(step.getID());
          newStepStep.setGroupID(new Integer(++groupCount));
          newStepStep.setChildStepID(endStepID);
          newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
          stepStepDAO.insertObject(newStepStep);
        }
      }
      
      //update the parentType of the mtmWorkflowStepWorkflowStep
      childList = stepStepDAO.getListByParentStepID(step.getID());
      for(int n=0; n<childList.size(); n++){
        MtmWorkflowStepWorkflowStep tmpStepStep = (MtmWorkflowStepWorkflowStep)childList.get(n);
        tmpStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE+n);
        stepStepDAO.updateObject(tmpStepStep);
      }
    }
    
    if(!Utility.isEmpty(step.getParentStepIDs())){
      this.handelMergeStep(step);
    }
    
    //* move all directly step to the first of the possible routes if step type is updated
    if (!oldStep.getActionType().equals(step.getActionType())) {
      
      boolean isOldApprovel = WorkflowStep.ACTION_TYPE_APPROVAL.equals(oldStep.getActionType());
      boolean isOldTODO = WorkflowStep.ACTION_TYPE_TASK_TO_DO.equals(oldStep.getActionType());
      boolean isOldBoolean = WorkflowStep.ACTION_TYPE_BOOLEAN.equals(oldStep.getActionType());
      boolean isNewApproval = WorkflowStep.ACTION_TYPE_APPROVAL.equals(step.getActionType());     
      boolean isNewToDo = WorkflowStep.ACTION_TYPE_TASK_TO_DO.equals(step.getActionType());
      boolean isNewBoolean = WorkflowStep.ACTION_TYPE_BOOLEAN.equals(step.getActionType());
      
      if((isOldTODO && isNewApproval) || (isOldApprovel && isNewToDo) || (isOldTODO&&isNewBoolean)||
          (isOldBoolean&&isNewToDo) || (isOldApprovel&&isNewBoolean) || (isOldBoolean&&isNewApproval)){
        this.updateWorkflowStepActionType(oldStep,step);
      }else{
        for (int i=0; i<childList.size(); i++) {
          MtmWorkflowStepWorkflowStep childStepStep = (MtmWorkflowStepWorkflowStep) childList.get(i);
          if (isNewApproval) {
            childStepStep.setParentType(WorkflowStep.PARENT_TYPE_APPROVED);
          } else if (isNewBoolean) {
            childStepStep.setParentType(WorkflowStep.PARENT_TYPE_YES);
          } else if (WorkflowStep.ACTION_TYPE_SYSTEM_AUTO.equals(step.getActionType())) {
            WorkflowAction tmpAction = (WorkflowAction) actionDAO.getObjectByID(step.getActionID());
            WorkflowProcessor processor = AutomaticTaskFactory.getInstance(tmpAction);
            int totalRoutine = processor.getNumOfRoutine();
            
            if (totalRoutine>1) {
              childStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE + 0);
            } else {
              childStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE);
            }
          } else if (WorkflowStep.ACTION_TYPE_USER_DEFINED_TASK.equals(step.getActionType())) {
            childStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE + 0);
          } else if (WorkflowStep.ACTION_TYPE_FORM_DRIVEN.equals(step.getActionType())) {
            //childStepStep.setParentType(WorkflowStep.PARENT_TYPE_FROM_VALUE_DRIVEN);
            childStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE + 0);
          } else {
            childStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE);
          }
          stepStepDAO.updateObject(childStepStep);
        }
        if (childList.size() > 1) {
          Map parentStepChildStepMap = new HashMap();
          for (int i=0; i<childList.size(); i++) {
            MtmWorkflowStepWorkflowStep childStepStep = (MtmWorkflowStepWorkflowStep) childList.get(i);
            
            if ((MtmWorkflowStepWorkflowStep) parentStepChildStepMap.get(childStepStep.getParentStepID()+"-"+childStepStep.getParentType()+"-"+childStepStep.getChildStepID())!=null) {
              stepStepDAO.deleteObject(childStepStep);
            } else {
              parentStepChildStepMap.put(childStepStep.getParentStepID()+"-"+childStepStep.getParentType()+"-"+childStepStep.getChildStepID(), childStepStep);
            }
          }
        }
      }
    }*/
    WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(this.sessionContainer, this.dbConn);
    MtmWorkflowStepUserActorDAObject stepActorDAO = new MtmWorkflowStepUserActorDAObject(this.sessionContainer, this.dbConn);
    WorkflowStep oldStep = (WorkflowStep) stepDAO.getObjectByID(step.getID());
    
    step = updateWorkflowStepBasicInfo(step);
    //delete old MtmWorkflowStepUserActor record
    stepActorDAO.deleteListByWorkflowStepID(step.getID());
    //  create user actor for the step
    step = createWorkflowStepUserInfo(step);
    //update workflowStep workflowStep relation Ship
    step = updateWorkflowStepStepRelationShip(step,oldStep);
    
    return step;
  }
  
  /**
   * update WorkflowStep basic Information
   * @param step
   * @return
   * @throws ApplicationException
   */
  private WorkflowStep updateWorkflowStepBasicInfo(WorkflowStep step) throws ApplicationException{
    WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(sessionContainer, dbConn);
    step = setWorkflowStepBasicInfo(step);
    step = (WorkflowStep)stepDAO.updateObject(step);
    return step;
  }
  /**
   * update mtmWorkflowStep workflowStep relation ship
   * @param step
   * @return
   * @throws ApplicationException
   */
  private WorkflowStep updateWorkflowStepStepRelationShip(WorkflowStep step,WorkflowStep oldStep) throws ApplicationException{
    MtmWorkflowStepWorkflowStepDAObject stepStepDAO = new MtmWorkflowStepWorkflowStepDAObject(this.sessionContainer, this.dbConn);
    WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(this.sessionContainer, this.dbConn);
    WorkflowActionDAObject actionDAO = new WorkflowActionDAObject(sessionContainer, dbConn);
    WorkflowOperationManager wfOperationManager = new WorkflowOperationManager(this.sessionContainer, this.dbConn);
    
    //get the routineListValue
    String routeListValue = step.getRouteListValue();
    String[] routineArr = null;
    if(!Utility.isEmpty(routeListValue) && !"null".equals(routeListValue)){
      routineArr = TextUtility.splitString(routeListValue,"|");
    }
    
    if(WorkflowStep.ACTION_TYPE_FORM_DRIVEN.equals(oldStep.getActionType()) ){
      if(!Utility.isEmpty(step.getFormEquationListValue()) && !"null".equals(step.getFormEquationListValue())){
        routineArr = TextUtility.splitString(step.getFormEquationListValue(),"$$");
      }
    }
    
    //* update workflow step workflow step relation
    List childList = stepStepDAO.getListByParentStepID(step.getID());
    if (WorkflowStep.ACTION_TYPE_USER_DEFINED_TASK.equals(oldStep.getActionType()) ||
        WorkflowStep.ACTION_TYPE_FORM_DRIVEN.equals(oldStep.getActionType()) ) {
      if( !Utility.isEmpty(routineArr) && routineArr.length < childList.size()  ) {
        //delete the remove rountine
        for(int j=routineArr.length; j<childList.size(); j++ ){
          MtmWorkflowStepWorkflowStep tmpStepStep = (MtmWorkflowStepWorkflowStep)childList.get(j);
         if(!WorkflowStep.ACTION_TYPE_END.equals(((WorkflowStep)stepDAO.getObjectByID(tmpStepStep.getChildStepID())).getActionType())){
           wfOperationManager.deleteAfterSteps(tmpStepStep.getChildStepID(),tmpStepStep.getParentStepID());
          }else{
            stepStepDAO.deleteObject(tmpStepStep);
          }
         }
      } else if (!Utility.isEmpty(routineArr) && routineArr.length > childList.size() ){
        //get the end step id
        List stepList = stepDAO.getListByWorkflowRecordID(step.getWorkflowRecordID());
        Integer endStepID = null;
        for(int i=0; i<stepList.size(); i++){
          WorkflowStep tmpStep = (WorkflowStep)stepList.get(i);
          if(WorkflowStep.END_STEP_SEQ_NO.equals(tmpStep.getStepSeq())){
            endStepID = tmpStep.getID();
          }
        }
        
        // get the max group ID from Mtm_WF_Step_WF_Step table
        int groupCount = stepStepDAO.getMaxGroupIDByWorkflowRecordID(step.getWorkflowRecordID()).intValue();
        
        for(int k=childList.size(); k<routineArr.length; k++){
          MtmWorkflowStepWorkflowStep newStepStep = new MtmWorkflowStepWorkflowStep();
          newStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE+k);
          newStepStep.setParentStepID(step.getID());
          newStepStep.setGroupID(new Integer(++groupCount));
          newStepStep.setChildStepID(endStepID);
          newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
          stepStepDAO.insertObject(newStepStep);
        }
      }
      
      //update the parentType of the mtmWorkflowStepWorkflowStep
      childList = stepStepDAO.getListByParentStepID(step.getID());
      for(int n=0; n<childList.size(); n++){
        MtmWorkflowStepWorkflowStep tmpStepStep = (MtmWorkflowStepWorkflowStep)childList.get(n);
        tmpStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE+n);
        stepStepDAO.updateObject(tmpStepStep);
      }
    }
    
    if(!Utility.isEmpty(step.getParentStepIDs())){
      this.handelMergeStep(step);
    }
    
    //* move all directly step to the first of the possible routes if step type is updated
    if (!oldStep.getActionType().equals(step.getActionType())) {
      
      boolean isOldApprovel = WorkflowStep.ACTION_TYPE_APPROVAL.equals(oldStep.getActionType());
      boolean isOldTODO = WorkflowStep.ACTION_TYPE_TASK_TO_DO.equals(oldStep.getActionType());
      boolean isOldBoolean = WorkflowStep.ACTION_TYPE_BOOLEAN.equals(oldStep.getActionType());
      boolean isNewApproval = WorkflowStep.ACTION_TYPE_APPROVAL.equals(step.getActionType());     
      boolean isNewToDo = WorkflowStep.ACTION_TYPE_TASK_TO_DO.equals(step.getActionType());
      boolean isNewBoolean = WorkflowStep.ACTION_TYPE_BOOLEAN.equals(step.getActionType());
      
      if((isOldTODO && isNewApproval) || (isOldApprovel && isNewToDo) || (isOldTODO&&isNewBoolean)||
          (isOldBoolean&&isNewToDo) || (isOldApprovel&&isNewBoolean) || (isOldBoolean&&isNewApproval)){
        this.updateWorkflowStepActionType(oldStep,step);
      }else{
        for (int i=0; i<childList.size(); i++) {
          MtmWorkflowStepWorkflowStep childStepStep = (MtmWorkflowStepWorkflowStep) childList.get(i);
          if (isNewApproval) {
            childStepStep.setParentType(WorkflowStep.PARENT_TYPE_APPROVED);
          } else if (isNewBoolean) {
            childStepStep.setParentType(WorkflowStep.PARENT_TYPE_YES);
          } else if (WorkflowStep.ACTION_TYPE_SYSTEM_AUTO.equals(step.getActionType())) {
            WorkflowAction tmpAction = (WorkflowAction) actionDAO.getObjectByID(step.getActionID());
            WorkflowProcessor processor = AutomaticTaskFactory.getInstance(tmpAction);
            int totalRoutine = processor.getNumOfRoutine();
            
            if (totalRoutine>1) {
              childStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE + 0);
            } else {
              childStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE);
            }
          } else if (WorkflowStep.ACTION_TYPE_USER_DEFINED_TASK.equals(step.getActionType())) {
            childStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE + 0);
          } else if (WorkflowStep.ACTION_TYPE_FORM_DRIVEN.equals(step.getActionType())) {
            childStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE + 0);
          } else {
            childStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE);
          }
          stepStepDAO.updateObject(childStepStep);
        }
        if (childList.size() > 1) {
          Map parentStepChildStepMap = new HashMap();
          for (int i=0; i<childList.size(); i++) {
            MtmWorkflowStepWorkflowStep childStepStep = (MtmWorkflowStepWorkflowStep) childList.get(i);
            
            if ((MtmWorkflowStepWorkflowStep) parentStepChildStepMap.get(childStepStep.getParentStepID()+"-"+childStepStep.getParentType()+"-"+childStepStep.getChildStepID())!=null) {
              if(!WorkflowStep.ACTION_TYPE_END.equals(((WorkflowStep)stepDAO.getObjectByID(childStepStep.getChildStepID())).getActionType())){
                  wfOperationManager.deleteAfterSteps(childStepStep.getChildStepID(),childStepStep.getParentStepID());
                }else{
                  stepStepDAO.deleteObject(childStepStep);
                }
              } else {
              parentStepChildStepMap.put(childStepStep.getParentStepID()+"-"+childStepStep.getParentType()+"-"+childStepStep.getChildStepID(), childStepStep);
            }
          }
        }
      }
    }
    return step;
  }
  /**
   * Creates a loop back (route to) step mapping for the given step
   *
   * @param step The current step
   * @return The loop back mapping
   * @throws ApplicationException
   * @see com.dcivision.bean.MtmWorkflowStepWorkflowStep
   */
  public MtmWorkflowStepWorkflowStep createLoopBackWorkflowStep(WorkflowStep step) throws ApplicationException {
    MtmWorkflowStepWorkflowStepDAObject stepStepDAO = new MtmWorkflowStepWorkflowStepDAObject(sessionContainer, dbConn);
    MtmWorkflowStepWorkflowStep mtmWorkflowStepWorkflowStep = new MtmWorkflowStepWorkflowStep();
    MtmWorkflowStepWorkflowStep oldStepStep;
    String parentType = step.getParentType();
    Integer parentStepID = step.getParentStepID();
    Integer childStepID = step.getLoopBackStepID();
    log.debug("parent type: "+parentType);
    log.debug("parent step id: "+parentStepID);
    log.debug("child step id: "+childStepID);
    
    Integer nextGroupID = stepStepDAO.getNextGroupIDForWorkflowRecord(step.getWorkflowRecordID());
//    List childStepList = stepStepDAO.getListByParentIDChildIDAndParentType(parentStepID, null, parentType);//tc.delete:here need childStepID ,if childStepID is null will delete all childSteps of parentStep
    List childStepList = stepStepDAO.getListByParentIDChildIDAndParentType(parentStepID, step.getCurrentStepID(), parentType);
    for (int i = 0; i < childStepList.size(); i++) {
      MtmWorkflowStepWorkflowStep oldChildStepStep = (MtmWorkflowStepWorkflowStep)childStepList.get(i);
      stepStepDAO.deleteObject(oldChildStepStep);
    }
    
    mtmWorkflowStepWorkflowStep.setParentType(parentType);
    mtmWorkflowStepWorkflowStep.setParentStepID(parentStepID);
    mtmWorkflowStepWorkflowStep.setChildStepID(childStepID);
    mtmWorkflowStepWorkflowStep.setGroupID(nextGroupID);
    mtmWorkflowStepWorkflowStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_YES);
    mtmWorkflowStepWorkflowStep.setLoopBackAllBranch(step.getLoopBackALLOffset());
    mtmWorkflowStepWorkflowStep = (MtmWorkflowStepWorkflowStep) stepStepDAO.insertObject(mtmWorkflowStepWorkflowStep);
    
    // loopback other offset step;
    /*
     if (GlobalConstant.TRUE.equals(step.getLoopBackALLOffset())) {
     List offSetStepList = this.getHaveCollocateStepList(step.getLoopBackStepID());
     for (int i = 0; i < offSetStepList.size(); i++) {
     MtmWorkflowStepWorkflowStep stepStep = (MtmWorkflowStepWorkflowStep)offSetStepList.get(i);
     this.createLoopbackStep(stepStep, step.getLoopBackStepID());
     }
     }
     */
    return mtmWorkflowStepWorkflowStep;
  }
  /**
   * Creates a end step mapping for the given step
   *
   * @param step The current step
   * @return The end step mapping
   * @throws ApplicationException
   * @see com.dcivision.bean.MtmWorkflowStepWorkflowStep
   */
  public MtmWorkflowStepWorkflowStep insertEndStepLinkAfterStep(WorkflowStep step) throws ApplicationException {
    MtmWorkflowStepWorkflowStepDAObject stepStepDAO = new MtmWorkflowStepWorkflowStepDAObject(sessionContainer, dbConn);
    MtmWorkflowStepWorkflowStep mtmWorkflowStepWorkflowStep = new MtmWorkflowStepWorkflowStep();
    WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(sessionContainer, dbConn);
    WorkflowStep endStep = (WorkflowStep) stepDAO.getEndStepByWorkflowRecordID(step.getWorkflowRecordID());
    
    String parentType = step.getParentType();
    Integer parentStepID = step.getParentStepID();
    Integer childStepID = endStep.getID();
    
    List currentStepToEndStepList = stepStepDAO.getListByParentIDChildIDAndParentType(parentStepID, childStepID, parentType);
    List currentStepToStepList = stepStepDAO.getListByParentIDChildIDAndParentType(parentStepID, null, parentType);
    List currentLoopBackStepList = stepStepDAO.getListByParentStepIDAndParentTypeAndLoopBack(parentStepID, parentType, GlobalConstant.TRUE);
    if (currentStepToEndStepList.size() > 0) {
      throw new ApplicationException(WorkflowErrorConstant.STEP_ALREADY_LINK_TO_END_STEP);
      //return null;
    }
    
    log.debug("parent type: "+parentType);
    log.debug("parent step id: "+parentStepID);
    log.debug("child step id: "+childStepID);
    log.debug("the current step have: " + currentStepToStepList.size() + " childs");
    // if cerrent step after is only loopback step ,delete this loopback step
    if (currentStepToStepList.size() == 1 && currentLoopBackStepList.size() > 0) {
      MtmWorkflowStepWorkflowStep stepToLoopBackStep = (MtmWorkflowStepWorkflowStep)currentLoopBackStepList.get(0);
      stepStepDAO.deleteObject(stepToLoopBackStep);
    }
    
    Integer nextGroupID = stepStepDAO.getNextGroupIDForWorkflowRecord(step.getWorkflowRecordID());
    if (nextGroupID == null) {
      nextGroupID = new Integer(1);
    }
    
    mtmWorkflowStepWorkflowStep.setParentType(parentType);
    mtmWorkflowStepWorkflowStep.setParentStepID(parentStepID);
    mtmWorkflowStepWorkflowStep.setChildStepID(childStepID);
    mtmWorkflowStepWorkflowStep.setGroupID(nextGroupID);
    mtmWorkflowStepWorkflowStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_YES);
    mtmWorkflowStepWorkflowStep = (MtmWorkflowStepWorkflowStep) stepStepDAO.insertObject(mtmWorkflowStepWorkflowStep);
    
    return mtmWorkflowStepWorkflowStep;
  }
  /**
   * Deletes a single step by the given step ID, and thus the step owners and related rules
   *
   * @param workflowStepID The ID of the WorkflowStep to be deleted
   * @throws ApplicationException
   */
  public void deleteSingleWorkflowStep(Integer workflowStepID) throws ApplicationException {
    WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(sessionContainer, dbConn);
    MtmWorkflowStepWorkflowStepDAObject stepStepDAO = new MtmWorkflowStepWorkflowStepDAObject(sessionContainer, dbConn);
    MtmWorkflowStepUserActorDAObject stepUserActorDAO = new MtmWorkflowStepUserActorDAObject(sessionContainer, dbConn);
    MtmWorkflowStepPriorityRuleDAObject priorityRuleDAO = new MtmWorkflowStepPriorityRuleDAObject(sessionContainer, dbConn);
    WorkflowStepRouteDAObject routeDAO=new WorkflowStepRouteDAObject(sessionContainer, dbConn);
    WorkflowStageDAObject stageDAO=new WorkflowStageDAObject(sessionContainer, dbConn);
    WorkflowOperationCheckManager checkManager = new WorkflowOperationCheckManager(sessionContainer, dbConn);
    
    //* get all the parent workflow steps
    List parentStepList = stepStepDAO.getListByChildStepID(workflowStepID);
    //* get all the Child workflow steps
    List childStepList = stepStepDAO.getListByParentStepID(workflowStepID);
    String isExec = GlobalConstant.TRUE;
    
    WorkflowStep workflowStep = (WorkflowStep) stepDAO.getObjectByID(workflowStepID);
    WorkflowStep endStep = (WorkflowStep)stepDAO.getEndStepByWorkflowRecordID(workflowStep.getWorkflowRecordID());
    
    //check if can operate
    checkManager.checkIfCanDeleteCurrentStep(workflowStepID);
    //add other check here
    //check end
    
    if (!Utility.isEmpty(parentStepList)) {
      //for(int j=0;j<parentStepList.size();j++){
      while(parentStepList!=null&&parentStepList.size()>0){
    	  parentStepList = stepStepDAO.getListByChildStepID(workflowStepID);
    	  if(parentStepList!=null && parentStepList.size()>0){
		      MtmWorkflowStepWorkflowStep tmpStepStep = (MtmWorkflowStepWorkflowStep) parentStepList.get(0);
		      if(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO.equals(tmpStepStep.getLoopBack())){
			      if (!Utility.isEmpty(childStepList)) { 
			        while(childStepList.size()>0) {
			          childStepList = stepStepDAO.getListByParentStepID(workflowStepID);
			          if(childStepList!=null && childStepList.size()>0){
			            MtmWorkflowStepWorkflowStep childStepStep = (MtmWorkflowStepWorkflowStep)childStepList.get(0);
			            if (WorkflowStep.ACTION_TYPE_APPROVAL.equals(workflowStep.getActionType()) ||
			                WorkflowStep.ACTION_TYPE_BOOLEAN.equals(workflowStep.getActionType()) ||
			                WorkflowStep.ACTION_TYPE_USER_DEFINED_TASK.equals(workflowStep.getActionType()) ||
			                WorkflowStep.ACTION_TYPE_FORM_DRIVEN.equals(workflowStep.getActionType())||
                      endStep.getID().equals(childStepStep.getChildStepID())){
			              this.deleteWorkflowSteps(workflowStep.getID(), tmpStepStep.getParentStepID());
			              isExec = GlobalConstant.FALSE;
			            }else{          
			              //childStepStep.setParentStepID(tmpStepStep.getParentStepID());              
			              //stepStepDAO.updateObject(childStepStep);
                    tmpStepStep.setChildStepID(childStepStep.getChildStepID());
                    tmpStepStep.setGroupID(childStepStep.getGroupID());//for bug-2023
                    stepStepDAO.updateObject(tmpStepStep);
                    stepStepDAO.deleteObject(childStepStep);//TC.add
			            }
			          }
			        }			        
			      }
		      }else if(MtmWorkflowStepWorkflowStep.LOOP_BACK_YES.equals(tmpStepStep.getLoopBack())){
		    	tmpStepStep.setChildStepID(endStep.getID());
		    	stepStepDAO.updateObject(tmpStepStep);
		      }
	    	}
      	}
      	List parentStepList1 = stepStepDAO.getListByChildStepID(workflowStepID);
	  	for(int i=0;parentStepList1!=null&&i<parentStepList1.size();i++){
	  		MtmWorkflowStepWorkflowStep  tmpStepStep1 = (MtmWorkflowStepWorkflowStep) parentStepList.get(i);
	  		stepStepDAO.deleteObject(tmpStepStep1);  
	  	}
    } 
    //* set the child step to link with the first parent step
    if (GlobalConstant.TRUE.equals(isExec)) {
      // delete this step
      stepDAO.deleteObject(workflowStep);
      // delete the step owner setting
      stepUserActorDAO.deleteListByWorkflowStepID(workflowStepID);
      // delete the step for MTM_WF_STEP_PRIORITY_RULE
      priorityRuleDAO.deletePriorityRuleByStepID(workflowStepID);
      // delete the step for mtm_wf_step_notify_target AND mtm_wf_step_notify_rule
      this.deleteNotifyRuleAndNotifyTargetByWorkflowStepID(workflowStepID);
      // delete the step for workflow_step_parameter_value
      this.deleteStepParameter(workflowStepID);
      // delete the step for workflow_step_route
      routeDAO.deleteByWorkflowStepID(workflowStepID);
      // delete the step for mtm_wf_stage_wf_step
      stageDAO.deleteByWorkflowStepID(workflowStepID);
      //end
      log.debug("Step object deleted, step="+workflowStepID);
    }
  }  
  /**
   * Deletes a single step by the given step ID, and thus the step owners and related rules
   *
   * @param workflowStepID The ID of the WorkflowStep to be deleted
   * @param connectedSubStepID If specified, connect this child step as the new child step of its original parent.
   * @throws ApplicationException
   */
  public void deleteSingleWorkflowStep(Integer workflowStepID,Integer connectedSubStepID) throws ApplicationException {
    WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(sessionContainer, dbConn);
    MtmWorkflowStepWorkflowStepDAObject stepStepDAO = new MtmWorkflowStepWorkflowStepDAObject(sessionContainer, dbConn);
    MtmWorkflowStepUserActorDAObject stepUserActorDAO = new MtmWorkflowStepUserActorDAObject(sessionContainer, dbConn);
    MtmWorkflowStepPriorityRuleDAObject priorityRuleDAO = new MtmWorkflowStepPriorityRuleDAObject(sessionContainer, dbConn);
    WorkflowStepRouteDAObject routeDAO=new WorkflowStepRouteDAObject(sessionContainer, dbConn);
    WorkflowStageDAObject stageDAO=new WorkflowStageDAObject(sessionContainer, dbConn);
    WorkflowOperationCheckManager checkManager = new WorkflowOperationCheckManager(sessionContainer, dbConn);
    
    //* get all the parent workflow steps
    List parentStepList = stepStepDAO.getListByChildStepID(workflowStepID);
    //* get all the Child workflow steps
    List childStepList = stepStepDAO.getListByParentStepID(workflowStepID);
    String isExec = GlobalConstant.TRUE;
    
    WorkflowStep workflowStep = (WorkflowStep) stepDAO.getObjectByID(workflowStepID);
    WorkflowStep endStep = (WorkflowStep)stepDAO.getEndStepByWorkflowRecordID(workflowStep.getWorkflowRecordID());
//  check if can operate
    checkManager.checkIfCanDeleteCurrentStep(workflowStepID);
    //add other check here
    //check end
    MtmWorkflowStepWorkflowStep workflowStepConnectionStep = null;
    for(int i=0;i<childStepList.size();i++){
      MtmWorkflowStepWorkflowStep tmpStepStep = (MtmWorkflowStepWorkflowStep)childStepList.get(i);
      if(tmpStepStep.getChildStepID().equals(connectedSubStepID)){
        workflowStepConnectionStep = tmpStepStep;
        break;
      }
    }
    //---get the workflowStep-connectionStep ship -- end
    
    if (!Utility.isEmpty(parentStepList)) {
      for (int h = 0; h < parentStepList.size(); h++) {
        MtmWorkflowStepWorkflowStep tmpStepStep = (MtmWorkflowStepWorkflowStep) parentStepList.get(h);
        if(!GlobalConstant.TRUE.equals(tmpStepStep.getLoopBack())){
          tmpStepStep.setChildStepID(connectedSubStepID);
          tmpStepStep.setGroupID(workflowStepConnectionStep.getGroupID());//for bug-2023
          stepStepDAO.updateObject(tmpStepStep);
        }else{
          tmpStepStep.setChildStepID(endStep.getID());
          stepStepDAO.updateObject(tmpStepStep);
        }
      }
      
      while(childStepList.size()>0) {
        childStepList = stepStepDAO.getListByParentStepID(workflowStepID);
        if(childStepList!=null&&childStepList.size()>0){
          MtmWorkflowStepWorkflowStep tmpStepStep = (MtmWorkflowStepWorkflowStep) childStepList.get(0);
          // if tmpStepStep is childStep is not end step and is not reserve step,deleted it and after all step info;
          if (!Utility.isEmpty(connectedSubStepID) && !tmpStepStep.getChildStepID().equals(connectedSubStepID) && !endStep.getID().equals(tmpStepStep.getChildStepID()) && !"Y".equals(tmpStepStep.getLoopBack())) {
            this.deleteWorkflowSteps(workflowStep.getID(), tmpStepStep.getParentStepID());
            isExec = GlobalConstant.FALSE;
          } else {
            //deleted current stepStep delected mtm_wf_step_wf_step record
            stepStepDAO.deleteObject(tmpStepStep);
          }
        }
      }
    }
//  * set the child step to link with the first parent step
    if (GlobalConstant.TRUE.equals(isExec)) {
      childStepList = stepStepDAO.getListByParentStepID(workflowStepID);
      for (int i=0; i<childStepList.size(); i++) {
        MtmWorkflowStepWorkflowStep tmpStepStep = (MtmWorkflowStepWorkflowStep) childStepList.get(i);
        // if tmpStepStep is childStep is not end step and is not reserve step,deleted it and after all step info;
        if (!Utility.isEmpty(connectedSubStepID) && !tmpStepStep.getChildStepID().equals(connectedSubStepID) && !endStep.getID().equals(tmpStepStep.getChildStepID()) && !"Y".equals(tmpStepStep.getLoopBack())) {
          this.deleteWorkflowSteps(tmpStepStep.getChildStepID(), tmpStepStep.getParentStepID());
        }
      }
      // delete this step
      stepDAO.deleteObject(workflowStep);
      // delete the step owner setting
      stepUserActorDAO.deleteListByWorkflowStepID(workflowStepID);
      // delete the step for MTM_WF_STEP_PRIORITY_RULE
      priorityRuleDAO.deletePriorityRuleByStepID(workflowStepID);
      // delete the step for mtm_wf_step_notify_target AND mtm_wf_step_notify_rule
      this.deleteNotifyRuleAndNotifyTargetByWorkflowStepID(workflowStepID);
      // delete the step for workflow_step_parameter_value
      this.deleteStepParameter(workflowStepID);
      // delete the step for workflow_step_route
      routeDAO.deleteByWorkflowStepID(workflowStepID);
      // delete the step for mtm_wf_stage_wf_step
      stageDAO.deleteByWorkflowStepID(workflowStepID);
      //end
      log.debug("Step object deleted, step="+workflowStepID);
    }
  }
  //added by nancy
  /**
   * Deletes parameter(s) by the given workflow step ID
   *
   * @param workflowStepID The ID of the WorkflowStep to be deleted
   * @throws ApplicationException
   */
  public void deleteStepParameter(Integer workflowStepID)throws ApplicationException {
    WorkflowStepParameterValueDAObject wfParameterValueDAO=new WorkflowStepParameterValueDAObject(this.sessionContainer, this.dbConn);
    List parameterList = wfParameterValueDAO.getListByStepID(workflowStepID);
    for(int n=0; n<parameterList.size(); n++){
      WorkflowStepParameterValue parameterValue = (WorkflowStepParameterValue)parameterList.get(n);
      parameterValue.setWorkflowStepID(parameterValue.getID());
      wfParameterValueDAO.deleteObject(parameterValue);
    }
  }
  //end
 
  //TC.ADD FOR MOVE STEPS 
  public void moveWorkflowStepsTree(WorkflowStep workflowStep,Integer connectStepID,String connectStepType,Integer newParentID,String newParentType,Integer origParentID,String origParentType,boolean bMoveSubsequent,String operateTye) throws ApplicationException{
    if("P".equals(operateTye)){
    // add parallel Step after newParentStep  
      
      moveStepsToCurrentStepUseAddOperation(workflowStep,connectStepID,connectStepType,newParentID,newParentType,origParentID,origParentType,bMoveSubsequent);
    }else if("I".equals(operateTye)){
    // Insert workflowStep between newParentStep and newParentStep's currentChildStep 
      
      moveStepsToCurrentStepUseInsertOperation(workflowStep,connectStepID,connectStepType,newParentID,newParentType,origParentID,origParentType,bMoveSubsequent);
    }
    //check if move operation is successful
    WorkflowOperationCheckManager checkManager = new WorkflowOperationCheckManager(this.sessionContainer,this.dbConn);
    checkManager.checkIfMoveSingleStepSuccessfull(workflowStep.getID());
  }
  
  public void moveStepsToCurrentStepUseAddOperation(WorkflowStep workflowStep,Integer connectStepID,String connectStepType,Integer newParentID,String newParentType,Integer origParentID,String origParentType,boolean bMoveSubsequent) throws ApplicationException{
    // add parallel Step after new ParentStep
    MtmWorkflowStepWorkflowStepDAObject stepStepDAO = new MtmWorkflowStepWorkflowStepDAObject(sessionContainer, dbConn);
    WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(sessionContainer, dbConn);
    WorkflowStep tmpStep = (WorkflowStep)stepDAO.getObjectByID(workflowStep.getCurrentStepID());
    workflowStep.setID(tmpStep.getID());
    workflowStep.setActionType(tmpStep.getActionType());
    /*
    if(bMoveSubsequent){
      //move currentStep and its subSteps to newParentStep
      
    }else{
      //move currentStep to new ParentStep
    }//if should do something special please rewrite this code
    */
    
    //execute move form origParentStep
    moveStepsFormCurrentStep(workflowStep,origParentID,origParentType,bMoveSubsequent);
    
    //Add After the newParentStep
    List newParentStepStepList = stepStepDAO.getListByParentStepIDAndParentType(newParentID, newParentType);
    MtmWorkflowStepWorkflowStep newParentStepStep = (MtmWorkflowStepWorkflowStep)newParentStepStepList.get(0);
    MtmWorkflowStepWorkflowStep newParentStepCurrentStep = (MtmWorkflowStepWorkflowStep)newParentStepStep.clone();
    newParentStepCurrentStep.setChildStepID(workflowStep.getCurrentStepID());
    newParentStepCurrentStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
    stepStepDAO.insertObject(newParentStepCurrentStep);
    
    //Add endStep
    if(!bMoveSubsequent){
      WorkflowStep endStep=(WorkflowStep)stepDAO.getEndStepByWorkflowRecordID(workflowStep.getWorkflowRecordID());
      List endStepList = stepStepDAO.getListByChildStepID(endStep.getID());
      MtmWorkflowStepWorkflowStep ParentChildStepRelation =  (MtmWorkflowStepWorkflowStep)endStepList.get(0);
      this.addEndStep(workflowStep, ParentChildStepRelation);
      //delete more end steps
      deleteMoreEndStep(workflowStep);
    }
  }
  
  public void moveStepsToCurrentStepUseInsertOperation(WorkflowStep workflowStep,Integer connectStepID,String connectStepType,Integer newParentID,String newParentType,Integer origParentID,String origParentType,boolean bMoveSubsequent) throws ApplicationException{
    // insert workflowStep between newParentStep and newParentStep's currentChildStep 
    MtmWorkflowStepWorkflowStepDAObject stepStepDAO = new MtmWorkflowStepWorkflowStepDAObject(sessionContainer, dbConn);
    WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(sessionContainer, dbConn);
    WorkflowStep tmpStep = (WorkflowStep)stepDAO.getObjectByID(workflowStep.getCurrentStepID());
    workflowStep.setID(tmpStep.getID());
    workflowStep.setActionType(tmpStep.getActionType());
    /*
    if(bMoveSubsequent){
      //move currentStep and its subSteps to newParentStep
      
    }else{
      //move currentStep to new ParentStep
    }//if should do something special please rewrite this code
    */
    
    //execute move form origParentStep
    moveStepsFormCurrentStep(workflowStep,origParentID,origParentType,bMoveSubsequent);
    
    //Insert the currentStep / subSteps between the newParentStep and its currentChildStep
    List newParentStepStepList = stepStepDAO.getListByParentStepIDAndParentType(newParentID, newParentType);
    
    MtmWorkflowStepWorkflowStep newParentStepStep = (MtmWorkflowStepWorkflowStep)newParentStepStepList.get(0);
    MtmWorkflowStepWorkflowStep newParentStepCurrentStep = (MtmWorkflowStepWorkflowStep)newParentStepStep.clone();
   
    // newParentStep'currentChildStep connect to connectStep
    newParentStepCurrentStep.setChildStepID(workflowStep.getCurrentStepID());
    newParentStepCurrentStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
    
    Integer nextGroupID = stepStepDAO.getNextGroupIDForWorkflowRecord(workflowStep.getWorkflowRecordID());
    newParentStepCurrentStep.setGroupID(nextGroupID);
    
    stepStepDAO.insertObject(newParentStepCurrentStep);
    
    //update newParentStep-Child-Step to ConnectStep-Child-Step
    for(int i=0;i<newParentStepStepList.size();i++){
      MtmWorkflowStepWorkflowStep tmpStepStep = (MtmWorkflowStepWorkflowStep)newParentStepStepList.get(i);
      tmpStepStep.setParentStepID(connectStepID);
      tmpStepStep.setParentType(connectStepType);
      stepStepDAO.updateObject(tmpStepStep);
    }
    
    //get connectStep-endStep-Ship
    if(bMoveSubsequent){
      MtmWorkflowStepWorkflowStep connectStepEndStep = (MtmWorkflowStepWorkflowStep)stepStepDAO.getParentStepToEndStepRecord(workflowStep.getWorkflowRecordID(), connectStepType, connectStepID);
      //delete connectStep-endStep-Ship
      stepStepDAO.deleteObject(connectStepEndStep);
    }
    
    //ADD END STEP
    if(!bMoveSubsequent){
      WorkflowStep endStep=(WorkflowStep)stepDAO.getEndStepByWorkflowRecordID(workflowStep.getWorkflowRecordID());
      List endStepList = stepStepDAO.getListByChildStepID(endStep.getID());
      MtmWorkflowStepWorkflowStep ParentChildStepRelation =  (MtmWorkflowStepWorkflowStep)endStepList.get(0);
      this.addEndStep(workflowStep, ParentChildStepRelation);
      //delete more end steps
      deleteMoreEndStep(workflowStep);
    }
  }
  
  private void moveStepsFormCurrentStep(WorkflowStep workflowStep,Integer origParentID,String origParentType,boolean bMoveSubsequent) throws ApplicationException{
    // move step / step and subSteps form  origParentStep
    WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(sessionContainer, dbConn);
    MtmWorkflowStepWorkflowStepDAObject stepStepDAO = new MtmWorkflowStepWorkflowStepDAObject(sessionContainer, dbConn);
    Integer reserveStepID = workflowStep.getReserveStepID();
    String reserveStepLoopBack = null;
    
    MtmWorkflowStepWorkflowStep parentStepStep = (MtmWorkflowStepWorkflowStep)stepStepDAO.getParentStepToChildStepRecord(workflowStep.getWorkflowRecordID(), origParentID, origParentType, workflowStep.getCurrentStepID());
    List currentStepChildStepList = stepStepDAO.getListByParentStepID(workflowStep.getCurrentStepID());
    
    if(!bMoveSubsequent){
      
      if(Utility.isEmpty(reserveStepID)){
      //get the step which will connect to origParentStep
        MtmWorkflowStepWorkflowStep tmpStepStep = (MtmWorkflowStepWorkflowStep)currentStepChildStepList.get(0);
        reserveStepID = tmpStepStep.getChildStepID();
        reserveStepLoopBack = tmpStepStep.getLoopBack();
      }
      //--get the currentType-reserveStep-Ship LoopBack
      if(Utility.isEmpty(reserveStepLoopBack)){
        List tmpStepStepList = stepStepDAO.getListByParentIDAndChildID(workflowStep.getCurrentStepID(), reserveStepID);
        MtmWorkflowStepWorkflowStep tmpStepStep = (MtmWorkflowStepWorkflowStep)tmpStepStepList.get(0);
        reserveStepLoopBack = tmpStepStep.getLoopBack();
      }
    
      //--update parentStep-currentStep-ship to parentStep-reserveStep-ship
      parentStepStep.setChildStepID(reserveStepID);
      parentStepStep.setLoopBack(reserveStepLoopBack);
      stepStepDAO.updateObject(parentStepStep);
    
      //--delete all childStepShip of currentStep
        for(int i=0;i<currentStepChildStepList.size();i++){
          MtmWorkflowStepWorkflowStep tmpChildStepStep = (MtmWorkflowStepWorkflowStep)currentStepChildStepList.get(i);
          stepStepDAO.deleteObject(tmpChildStepStep);
        }
      //--delete more end step--
       WorkflowStep origParentStep = (WorkflowStep)stepDAO.getObjectByID(origParentID);
       origParentStep.setCurrentStepID(origParentID);
       deleteMoreEndStep(origParentStep);
       
    }else{
      // is move currentStep and its subSteps to ForSteps
      // just connect endStep to the parentStep
      WorkflowStep endStep = (WorkflowStep)stepDAO.getEndStepByWorkflowRecordID(workflowStep.getWorkflowRecordID());        
      parentStepStep.setChildStepID(endStep.getID());
      parentStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
      stepStepDAO.updateObject(parentStepStep);
    }
  }
  
  private void deleteMoreEndStep(WorkflowStep workflowStep) throws ApplicationException{
    WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(sessionContainer, dbConn);
    MtmWorkflowStepWorkflowStepDAObject stepStepDAO = new MtmWorkflowStepWorkflowStepDAObject(sessionContainer, dbConn);
    WorkflowStep endStep = (WorkflowStep)stepDAO.getEndStepByWorkflowRecordID(workflowStep.getWorkflowRecordID());
    List childStepList = stepStepDAO.getListByParentStepID(workflowStep.getCurrentStepID());
    
    for(int i=0;i<childStepList.size();i++){
      MtmWorkflowStepWorkflowStep tmpStepStep = (MtmWorkflowStepWorkflowStep)childStepList.get(i);
      if(GlobalConstant.RECORD_STATUS_INACTIVE.equals(tmpStepStep.getRecordStatus())){
        continue;
      }
      for(int j=i+1;j<childStepList.size();j++){
        MtmWorkflowStepWorkflowStep tmpStepStepCompare = (MtmWorkflowStepWorkflowStep)childStepList.get(j);
        if(GlobalConstant.RECORD_STATUS_INACTIVE.equals(tmpStepStepCompare.getRecordStatus())){
          continue;
        }
        if(tmpStepStep.getParentType().equals(tmpStepStepCompare.getParentType())){
          //should delete the step-endStep-ship
          deleteEndStep(stepStepDAO,tmpStepStep,tmpStepStepCompare,endStep);
        }
      }
    }
  }
  private void deleteEndStep(MtmWorkflowStepWorkflowStepDAObject stepStepDAO,MtmWorkflowStepWorkflowStep stepStep1,MtmWorkflowStepWorkflowStep stepStep2,WorkflowStep endStep) throws ApplicationException{
    int deleteStep1Property = 0;
    int deleteStep2Property = 0;
    
    if(stepStep1.getChildStepID().equals(endStep.getID())){
      deleteStep1Property+=3;
      if(MtmWorkflowStepWorkflowStep.LOOP_BACK_YES.equals(stepStep1.getLoopBack())){
        deleteStep1Property++;
      }
    }
    if(stepStep2.getChildStepID().equals(endStep.getID())){
      deleteStep2Property+=3;
      if(MtmWorkflowStepWorkflowStep.LOOP_BACK_YES.equals(stepStep2.getLoopBack())){
        deleteStep2Property++;
      }
    }
    if(deleteStep1Property>deleteStep2Property&&deleteStep1Property>=3){
      //delete it
      stepStepDAO.deleteObject(stepStep1);
      stepStep1.setRecordStatus(GlobalConstant.RECORD_STATUS_INACTIVE);
      return;
    }
    if(deleteStep2Property>=deleteStep1Property&&deleteStep2Property>=3){
      //delete it
      stepStepDAO.deleteObject(stepStep2);
      stepStep2.setRecordStatus(GlobalConstant.RECORD_STATUS_INACTIVE);
      return; 
    }
   
  }
  //TC.ADD END
  /**
   * Moves the given step (and/or its subtree) to a new specified location
   *
   * @param workflowStep The current WorkflowStep object
   * @param newParentID The new parent ID of the step are to be moved to
   * @param parentType The mapping type of the new parent step (approve/reject/yes/no, etc)
   * @param origParentID The original parent ID
   * @param bMoveSubsequent Whether to move the whole tree of just a single step
   * @throws ApplicationException
   * @see com.dcivision.bean.MtmWorkflowStepWorkflowStep
   */
  /*public void moveWorkflowStepsTree(WorkflowStep workflowStep, Integer newParentID, String newParentType,Integer origParentID,boolean bMoveSubsequent) throws ApplicationException {
    WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(sessionContainer, dbConn);
    MtmWorkflowStepWorkflowStepDAObject stepStepDAO = new MtmWorkflowStepWorkflowStepDAObject(sessionContainer, dbConn);
    Integer reserveStepID = workflowStep.getReserveStepID();
    //* get all the parent workflow steps
    List parentStepStepList = stepStepDAO.getListByChildStepID(workflowStep.getCurrentStepID());
    //simon_add_S
    if(bMoveSubsequent){
      //update oldParentStep
      for (int i=0; i<parentStepStepList.size(); i++) {
        MtmWorkflowStepWorkflowStep parentStepStep = (MtmWorkflowStepWorkflowStep) parentStepStepList.get(i);
        List parentStep_ChildStepStepList = stepStepDAO.getListByParentStepIDAndParentType(parentStepStep.getParentStepID(), parentStepStep.getParentType());
        if(parentStep_ChildStepStepList.size()>0 && parentStep_ChildStepStepList.size()==1){
          WorkflowStep parentStep = (WorkflowStep)stepDAO.getObjectByID(parentStepStep.getParentStepID());
          if(WorkflowStep.ACTION_TYPE_SUBMIT.equals(parentStep.getActionType())){
            stepStepDAO.deleteObject(parentStepStep);
          }else{
            WorkflowStep endStep = (WorkflowStep)stepDAO.getEndStepByWorkflowRecordID(workflowStep.getWorkflowRecordID());        
            parentStepStep.setChildStepID(endStep.getID());
            stepStepDAO.updateObject(parentStepStep);
          }
        }else{
          stepStepDAO.deleteObject(parentStepStep);
        }
      }
      //update newParentStep
      List newParent_childStepList= stepStepDAO.getListByParentStepIDAndParentType(newParentID, newParentType);
      if(newParent_childStepList.size()>0){
        MtmWorkflowStepWorkflowStep newParent_ParentStepStep = (MtmWorkflowStepWorkflowStep) newParent_childStepList.get(0);
        if(WorkflowStep.PARENT_TYPE_SINGLE.equals(newParent_ParentStepStep.getParentType())){
          MtmWorkflowStepWorkflowStep tmpNewParent_ParentStepStep = (MtmWorkflowStepWorkflowStep)((MtmWorkflowStepWorkflowStep) newParent_childStepList.get(0)).clone();
          tmpNewParent_ParentStepStep.setChildStepID(workflowStep.getCurrentStepID());
          stepStepDAO.insertObject(tmpNewParent_ParentStepStep);
        }else{         
          newParent_ParentStepStep.setChildStepID(workflowStep.getCurrentStepID());
          stepStepDAO.updateObject(newParent_ParentStepStep);
        }
      }
      List newParent_childEndStepStepList= stepStepDAO.getListByParentStepID(newParentID);
      for(int k=0;k<newParent_childEndStepStepList.size();k++){
        MtmWorkflowStepWorkflowStep newParentStepStep = (MtmWorkflowStepWorkflowStep) newParent_childEndStepStepList.get(k); 
        WorkflowStep childStep = (WorkflowStep)stepDAO.getObjectByID(newParentStepStep.getChildStepID());
        if(WorkflowStep.ACTION_TYPE_END.equals(childStep.getActionType()) && WorkflowStep.PARENT_TYPE_SINGLE.equals(newParentStepStep.getParentType())){
          stepStepDAO.deleteObject(newParentStepStep);
        }
      }
    }else if(!bMoveSubsequent){
      //update stepStep for old parentStep    
      for (int i=0; i<parentStepStepList.size(); i++) {
        MtmWorkflowStepWorkflowStep parentStepStep = (MtmWorkflowStepWorkflowStep) parentStepStepList.get(i);
        //List parentStep_ParentStepStepList = stepStepDAO.getListByChildStepID(parentStepStep.getParentStepID());
        List childStepStepList = stepStepDAO.getListByParentStepID(workflowStep.getCurrentStepID());
        boolean isDelAllChildStepStep=true;
        for (int j=0; j<childStepStepList.size(); j++) {
          MtmWorkflowStepWorkflowStep childStepStep = (MtmWorkflowStepWorkflowStep) childStepStepList.get(j); 
          WorkflowStep endStep = (WorkflowStep)stepDAO.getEndStepByWorkflowRecordID(workflowStep.getWorkflowRecordID());
          if(endStep.getID().equals(childStepStep.getChildStepID())){
            stepStepDAO.deleteObject(childStepStep);
            //delete it: if parentType=s, and only have one child,It will connect to End Step at line:2262 TC.ADD
          }else{
            childStepStep.setLoopBack("N");//why N?
            childStepStep.setParentType(parentStepStep.getParentType());
            childStepStep.setParentStepID(parentStepStep.getParentStepID());
            stepStepDAO.updateObject(childStepStep);
            isDelAllChildStepStep=false;
          }
        }        
        if(isDelAllChildStepStep){
          List parent_childStepStepList = stepStepDAO.getListByParentStepID(parentStepStep.getParentStepID());
          if(parent_childStepStepList.size()==1 || !WorkflowStep.PARENT_TYPE_SINGLE.equals(parentStepStep.getParentType())){
            WorkflowStep endStep = (WorkflowStep)stepDAO.getEndStepByWorkflowRecordID(workflowStep.getWorkflowRecordID());     
            parentStepStep.setChildStepID(endStep.getID());
            stepStepDAO.updateObject(parentStepStep);
          }else{
            stepStepDAO.deleteObject(parentStepStep);//delete it
          }
        }else{
          stepStepDAO.deleteObject(parentStepStep);
          //delete it:because ready change the relateShip at line:2252 TC.ADD
        }
      }
      //update newParentStep
      List newParent_childStepList= stepStepDAO.getListByParentStepIDAndParentType(newParentID, newParentType);
      if(newParent_childStepList.size()>0){//have child       
        MtmWorkflowStepWorkflowStep newParent_ParentStepStep = (MtmWorkflowStepWorkflowStep) newParent_childStepList.get(0);
        //get the first P-To-C Ship
        if(WorkflowStep.PARENT_TYPE_SINGLE.equals(newParent_ParentStepStep.getParentType())){
          MtmWorkflowStepWorkflowStep tmpNewParent_ParentStepStep = (MtmWorkflowStepWorkflowStep)((MtmWorkflowStepWorkflowStep) newParent_childStepList.get(0)).clone();
          tmpNewParent_ParentStepStep.setChildStepID(workflowStep.getCurrentStepID());
          stepStepDAO.insertObject(tmpNewParent_ParentStepStep);
        }else{//why change to currentStep?
          //maybe should rewrite this logic code
          newParent_ParentStepStep.setChildStepID(workflowStep.getCurrentStepID());
          stepStepDAO.updateObject(newParent_ParentStepStep);
        }
        
        //add new endStep for new currentStep
        WorkflowStep endStep = (WorkflowStep)stepDAO.getEndStepByWorkflowRecordID(workflowStep.getWorkflowRecordID());
        List endStepList = stepStepDAO.getListByChildStepID(endStep.getID());
        MtmWorkflowStepWorkflowStep ParentChildStepRelation =  (MtmWorkflowStepWorkflowStep)endStepList.get(0);
        WorkflowStep currentStep = (WorkflowStep)stepDAO.getObjectByID(workflowStep.getCurrentStepID());
        workflowStep.setActionType(currentStep.getActionType());
        workflowStep.setActionID(currentStep.getActionID());
        this.addEndStep(workflowStep, ParentChildStepRelation);
      }
      List newParent_childEndStepStepList= stepStepDAO.getListByParentStepID(newParentID);
      for(int h=0;h<newParent_childEndStepStepList.size();h++){
        MtmWorkflowStepWorkflowStep newParent_childEndStepStep =((MtmWorkflowStepWorkflowStep) newParent_childEndStepStepList.get(h));
        WorkflowStep childStep = (WorkflowStep)stepDAO.getObjectByID(newParent_childEndStepStep.getChildStepID());
        if(WorkflowStep.ACTION_TYPE_END.equals(childStep.getActionType()) && WorkflowStep.PARENT_TYPE_SINGLE.equals(newParent_childEndStepStep.getParentType())){
          stepStepDAO.deleteObject(newParent_childEndStepStep);
        }
      }
    }
    //simon_add_end
  }*/
  
  /**********************************************************************
   * Start comment out by Lun ( 2005-01-20)
   * This block of code seems not used in anywhere
   **********************************************************************/
  //* delete workflow step workflow step relationship
  //* delete all steps after current step(s)
  //  public void deleteStepAfterCurrentStep(Integer currentStepID) throws ApplicationException {
  //    MtmWorkflowStepWorkflowStepDAObject stepStepDAO = new MtmWorkflowStepWorkflowStepDAObject(sessionContainer, dbConn);
  //    stepStepDAO.deleteListByParentStepIDLoopBackType(currentStepID, null);
  //  }
  
  //* delete loop back step after current step
  //  public void deleteLoopbackStepAfterCurrentStep(Integer currentStepID) throws ApplicationException {
  //    MtmWorkflowStepWorkflowStepDAObject stepStepDAO = new MtmWorkflowStepWorkflowStepDAObject(sessionContainer, dbConn);
  //    stepStepDAO.deleteListByParentStepIDLoopBackType(currentStepID, GlobalConstant.TRUE);
  //  }
  //* delete nono loop back step after current step
  //  public void deleteNonLoopbackStepAfterCurrentStep(Integer currentStepID) throws ApplicationException {
  //    MtmWorkflowStepWorkflowStepDAObject stepStepDAO = new MtmWorkflowStepWorkflowStepDAObject(sessionContainer, dbConn);
  //    stepStepDAO.deleteListByParentStepIDLoopBackType(currentStepID, GlobalConstant.FALSE);
  //  }
  /**********************************************************************
   * End comment out by Lun ( 2005-01-20)
   **********************************************************************/
  //simon---start  
  /**
   * Deletes this step (and the steps after) and reorganize the parent step mapping
   *
   * @param currentStepID The step (and its subtree) to be deleted
   * @param parentStepID The parent step ID of the step
   * @throws ApplicationException
   */
  public void deleteWorkflowSteps(Integer currentStepID, Integer parentStepID)throws ApplicationException {
   
    // Start re-organizing the mappings, the case need to cater includes,
    // 1. Delete the original parent/current step mapping
    // 2. Create an end step mapping for the approval/yes no/decision point/form driven route steps
    // 3. Handle the merged step
    WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(sessionContainer, dbConn);
    MtmWorkflowStepWorkflowStepDAObject stepStepDAO = new MtmWorkflowStepWorkflowStepDAObject(sessionContainer, dbConn);
    
    List mtmStepStepList = stepStepDAO.getListByChildStepID(currentStepID);//get the Parent_Current_Ship first,then in the method deleteAfterSteps(Integer id) will delete the ship
    // Recursively delete all the steps after
    deleteAfterSteps(currentStepID,parentStepID);
    
    // Create a End Step for each of its desired parent
    //List mtmStepStepList = stepStepDAO.getListByChildStepID(currentStepID);//simon_delete
    //List mtmStepStepList=stepStepDAO.getListByParentStepIDAndChildStepID(parentStepID,currentStepID);//simon_add
    if(!Utility.isEmpty(mtmStepStepList)){
      WorkflowStep dummyStep = (WorkflowStep)stepDAO.getObjectByID(parentStepID);
      WorkflowStep endStep = (WorkflowStep)stepDAO.getEndStepByWorkflowRecordID(dummyStep.getWorkflowRecordID());
      for ( int i = 0 ; i < mtmStepStepList.size() ; i++ ) {
        MtmWorkflowStepWorkflowStep stepStep = (MtmWorkflowStepWorkflowStep)mtmStepStepList.get(i);
        WorkflowStep parentStep = null;
        try{
          parentStep=(WorkflowStep)stepDAO.getObjectByID(stepStep.getParentStepID());
       
        String parentType = stepStep.getParentType();
        
        // Delete the old parent/child mapping
        log.debug("Deleting mapping entry for parent:"+parentStep.getID()+", child:"+currentStepID+", parentType="+parentType);
        //stepStepDAO.deleteObject(stepStep);//simon_delete
        
        // Create a end step mapping if there is no more relatives
        // Two rejected step of one approval step is regarded as relatives, while an approved and a rejected is not
        // For a rejected step, the parentType for the mtm_wf_step_wf_step is R, an approved step is A
        List siblingList = stepStepDAO.getListByParentStepIDAndParentType(parentStep.getID(), parentType);
        if ( Utility.isEmpty(siblingList) ) {
          Integer nextGroupID = stepStepDAO.getNextGroupIDForWorkflowRecord(parentStep.getWorkflowRecordID());
          if (Utility.isEmpty(nextGroupID)) {
            nextGroupID = new Integer(1) ;
          }
          MtmWorkflowStepWorkflowStep newStepStep = new MtmWorkflowStepWorkflowStep();
          newStepStep.setParentStepID(parentStep.getID());
          newStepStep.setParentType(parentType);
          //stepStep.setLoopBack(GlobalConstant.FALSE);
          newStepStep.setLoopBack(stepStep.getLoopBack());
          newStepStep.setChildStepID(endStep.getID());
          newStepStep.setGroupID(nextGroupID);
          newStepStep = (MtmWorkflowStepWorkflowStep)stepStepDAO.insertObject(newStepStep);
          log.debug("End step mapping created for parent="+parentStep.getID()+", parent type="+parentType+", mapping id="+newStepStep.getID());
        }else{
          log.debug("Ignoring end step mapping for parent="+parentStep.getID()+",parent type="+parentType);
        }
        
        }catch(RecordNotFoundException ex){
          log.debug("this step ready delete");         
        }
        
        // TODO: handle the after merge case
      }
      
    }else{
      log.debug("No parent step mapping to be reorganized");
    }
  }
  /**
   * Deletes the steps (and its owners, rules, etc) recursively
   *
   * @param currentStepID The step (and its subtree) to be deleted
   * @throws ApplicationException
   */
  public void deleteAfterSteps(Integer currentStepID,Integer parentStepID)throws ApplicationException {
    WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(sessionContainer, dbConn);
    MtmWorkflowStepWorkflowStepDAObject stepStepDAO = new MtmWorkflowStepWorkflowStepDAObject(sessionContainer, dbConn);
    MtmWorkflowStepUserActorDAObject stepUserActorDAO = new MtmWorkflowStepUserActorDAObject(sessionContainer, dbConn);
    MtmWorkflowStepPriorityRuleDAObject priorityRuleDAO = new MtmWorkflowStepPriorityRuleDAObject(sessionContainer, dbConn);
    WorkflowStepRouteDAObject routeDAO=new WorkflowStepRouteDAObject(sessionContainer, dbConn);
    WorkflowStageDAObject stageDAO=new WorkflowStageDAObject(sessionContainer, dbConn);
    
    List mtmStepStepList = stepStepDAO.getListByParentStepID(currentStepID);
    WorkflowStep currentStep = (WorkflowStep)stepDAO.getObjectByID(currentStepID);
    WorkflowStep endStep = (WorkflowStep)stepDAO.getEndStepByWorkflowRecordID(currentStep.getWorkflowRecordID());//Tc.add
    
    if(!Utility.isEmpty(mtmStepStepList)){
      log.debug("Try to delete all child of parent step="+currentStepID);
      for(int i=0; i<mtmStepStepList.size(); i++){
        log.debug("i>>>"+i);
        MtmWorkflowStepWorkflowStep mtmStepStep = (MtmWorkflowStepWorkflowStep)mtmStepStepList.get(i);
        log.debug("Try to delete child step, id="+mtmStepStep.getChildStepID());
        WorkflowStep childStep = null;
        try {
          childStep = (WorkflowStep)stepDAO.getObjectByID(mtmStepStep.getChildStepID());
        } catch (ApplicationException ex) {
          log.debug("no found child step!!!");
        }
        // Only proceed to delete the actual step object if it's not a end/loopback mapping
        if(!Utility.isEmpty(childStep) && !WorkflowStep.ACTION_TYPE_END.equals(childStep.getActionType()) &&
            !GlobalConstant.TRUE.equals(mtmStepStep.getLoopBack())){
          
          // walk through the sub-tree and delete all steps after
          deleteAfterSteps(mtmStepStep.getChildStepID(),mtmStepStep.getParentStepID());
          
        }else{
          log.debug("Loopback/End step found, ignoring deletion, id="+mtmStepStep.getChildStepID());
        }
        // Delete the mapping in MTM_WF_STEP_WF_STEP
        //simon_start
        List mtmStepSteByChildStepIDpList = stepStepDAO.getListByChildStepID(currentStepID);
        if(!Utility.isEmpty(mtmStepSteByChildStepIDpList)){  
          for ( int j = 0 ; j < mtmStepSteByChildStepIDpList.size() ; j++ ) {
            log.debug("j>>>"+j);
            MtmWorkflowStepWorkflowStep stepStep = (MtmWorkflowStepWorkflowStep)mtmStepSteByChildStepIDpList.get(j);
            // Delete the old parent/child mapping
            if(GlobalConstant.RECORD_STATUS_ACTIVE.equals(stepStep.getRecordStatus())){
              log.debug("delete stepStep by ChildStepID!   parentID="+stepStep.getParentStepID()+"   childID="+stepStep.getChildStepID());            
              //if(!GlobalConstant.TRUE.equals(stepStep.getLoopBack())){
              if(stepStep.getParentStepID().equals(parentStepID)){//in the sub tree
                stepStepDAO.deleteObject(stepStep);
              }else{//if not in the sub tree,just link to the end step.--#should not delete loopback parent-relate ship(just link it to end)
                stepStep.setChildStepID(endStep.getID());
                stepStepDAO.updateObject(stepStep);
              }
            }
          }
        }

        List new_mtmStepStepList = stepStepDAO.getListByParentStepID(currentStepID);
        for(int z=0; z<new_mtmStepStepList.size(); z++){
          MtmWorkflowStepWorkflowStep new_mtmStepStep = (MtmWorkflowStepWorkflowStep)new_mtmStepStepList.get(z);
          if(GlobalConstant.RECORD_STATUS_ACTIVE.equals(new_mtmStepStep.getRecordStatus())){
            log.debug("delete stepStep by ChildStepID!   parentID="+new_mtmStepStep.getParentStepID()+"   childID="+new_mtmStepStep.getChildStepID());
            stepStepDAO.deleteObject(new_mtmStepStep);
          }
        }
        //simon_end
      }
    }else{
      log.debug("No more child step: id=" + currentStepID );
    }
    // delete this step
    stepDAO.deleteObject(currentStep);
    // delete the step owner setting
    stepUserActorDAO.deleteListByWorkflowStepID(currentStepID);
    // delete the step for MTM_WF_STEP_PRIORITY_RULE
    priorityRuleDAO.deletePriorityRuleByStepID(currentStepID);
    // delete the step for mtm_wf_step_notify_target AND mtm_wf_step_notify_rule
    this.deleteNotifyRuleAndNotifyTargetByWorkflowStepID(currentStepID);
    // delete the step for workflow_step_parameter_value
    this.deleteStepParameter(currentStepID);
    // delete the step for workflow_step_route
    routeDAO.deleteByWorkflowStepID(currentStepID);
    // delete the step for mtm_wf_stage_wf_step
    stageDAO.deleteByWorkflowStepID(currentStepID);
    //end
    log.debug("Step object deleted, step="+currentStepID);
  }
  /**
   * Schedules the general notifications. For each notification in the given Vector, it
   * will store the Vector of objects into a JobDataMap for use in the scheduled jobs.
   *
   * The scheduledJobVec Vector is composed by a list of notications settings, which
   * themselves are again a Vector, for examples,
   * 
   * <pre>
   *   Vector notification = new Vector();
   *   notification.add(workflowStep);
   *   notification.add(workflowProgress);
   *   notification.add(notifyType);
   *   
   *   allNotifications.add ( notification );
   * </pre>
   *
   * @param scheduledJobVec Vector of the notifications to be scheduled
   * @throws ApplicationException
   * @see com.dcivision.workflow.core.MailScheduleManager
   * @see com.dcivision.workflow.bean.WorkflowStep#NOTIFY_OWNER_TASK_ARRIVED_BY_MAIL
   * @see com.dcivision.workflow.bean.WorkflowStep#NOTIFY_OWNER_TASK_ARRIVED_BY_SYSTEM
   * @see com.dcivision.workflow.bean.WorkflowStep#NOTIFY_ALL_TASK_COMPLETED_BY_MAIL
   * @see com.dcivision.workflow.bean.WorkflowStep#NOTIFY_ALL_TASK_COMPLETED_BY_SYSTEM
   * @see com.dcivision.workflow.bean.WorkflowStep#NOTIFY_OWNER_TASK_OVERDUE_BY_MAIL
   * @see com.dcivision.workflow.bean.WorkflowStep#NOTIFY_OWNER_TASK_OVERDUE_BY_SYSTEM
   * @see com.dcivision.workflow.bean.WorkflowStep#NOTIFY_ALL_TASK_OVERDUE_BY_MAIL
   * @see com.dcivision.workflow.bean.WorkflowStep#NOTIFY_ALL_TASK_OVERDUE_BY_SYSTEM
   * @see com.dcivision.workflow.bean.WorkflowStep#NOTIFY_ALL_TRACK_STOP
   */
  public synchronized void scheduleMail(Vector scheduledJobVec) throws ApplicationException {
    try {
      log.info("scheduledJobVec.size(): " + scheduledJobVec.size());
      Scheduler sched = SchedulerFactory.getScheduler();
      synchronized (sched) {
        HashMap map = new HashMap();
        for (int i = 0; i < scheduledJobVec.size(); i++) {
          WorkflowStep wstep = (WorkflowStep)((Vector)scheduledJobVec.get(i)).get(0);
          WorkflowProgress wprogress = (WorkflowProgress)((Vector)scheduledJobVec.get(i)).get(1);
          String processType = (String)((Vector)scheduledJobVec.get(i)).get(2);
          String workflowKey = processType + "_" + wprogress.getID().toString();
          if (map.get(workflowKey)!=null) {
            continue;
          } else {
            map.put(workflowKey, workflowKey);
          }
          
          log.info("******** Adding schedule for wprogress="+wprogress.getID());
          log.info("******** Key = "+workflowKey);
          JobDataMap dataMap = new org.quartz.JobDataMap();
          dataMap.put("workflowStep", wstep);
          dataMap.put("workflowProgress", wprogress);
          dataMap.put("processType", processType);
          JobDetail job = new JobDetail(GlobalConstant.JOB_PREFIX_WORKFLOW_NOTIFY+workflowKey, GlobalConstant.JOB_PREFIX_WORKFLOW_NOTIFY+"GROUP", Class.forName(SystemParameterFactory.getSystemParameter(SystemParameterConstant.WORKFLOW_NOTIFICATION_MANAGER)));
          job.setJobDataMap(dataMap);
          Timestamp scheduleDate = null;
          if (WorkflowStep.NOTIFY_OWNER_TASK_ARRIVED_BY_MAIL.equals(processType) || WorkflowStep.NOTIFY_OWNER_TASK_ARRIVED_BY_SYSTEM.equals(processType)
              || WorkflowStep.NOTIFY_ALL_TASK_COMPLETED_BY_MAIL.equals(processType) || WorkflowStep.NOTIFY_ALL_TASK_COMPLETED_BY_SYSTEM.equals(processType)
              || WorkflowStep.NOTIFY_ALL_TRACK_STOP.equals(processType) ) {
            scheduleDate = new Timestamp(System.currentTimeMillis() + 10000);
          } else if (WorkflowStep.NOTIFY_OWNER_TASK_OVERDUE_BY_MAIL.equals(processType) || WorkflowStep.NOTIFY_OWNER_TASK_OVERDUE_BY_SYSTEM.equals(processType)
              || WorkflowStep.NOTIFY_ALL_TASK_OVERDUE_BY_MAIL.equals(processType) || WorkflowStep.NOTIFY_ALL_TASK_OVERDUE_BY_SYSTEM.equals(processType) ) {
            
            if(GlobalConstant.TRUE.equals(wstep.getAllowAssignDueDate()) && !Utility.isEmpty(wprogress.getDueDate()) ){
              scheduleDate = wprogress.getDueDate();
            } else if (wstep.getOnNoResponseAfter() != null) {
              scheduleDate = wstep.getOnNoResponseAfter();
            } else if (wstep.getOnNoResponseFor() != null) {
              scheduleDate = Utility.addDay(wprogress.getCreateDate(),wstep.getOnNoResponseFor().intValue());
              Calendar tmpDate = Utility.timestampToCalendar(scheduleDate);
              //Utility.setCalendarTime(tmpDate, 23, 59, 59, 0);
              scheduleDate = Utility.calendarToTimestamp(tmpDate);
              // Commented by Brook.Huang. Mask working calendar count for [EIP-941].
              // if (!Utility.isEmpty(wstep.getOnNoResponseCalendarID()) ) {
              // HolidayManager holidayManager = new HolidayManager(this.sessionContainer, this.dbConn);
              // scheduleDate = holidayManager.getDueDateEndDate(Utility.getCurrentTimestamp(), wstep.getOnNoResponseFor().intValue()*24*60*60*1000,
              // new Integer(wstep.getOnNoResponseCalendarID()) );
              //              }  
            }
          }
          if (scheduleDate != null) {
            log.info("******** Schedule date: " + scheduleDate.toString());
            SimpleTrigger trigger = new SimpleTrigger(GlobalConstant.JOB_PREFIX_WORKFLOW_NOTIFY+workflowKey, GlobalConstant.JOB_PREFIX_WORKFLOW_NOTIFY+"GROUP",GlobalConstant.JOB_PREFIX_WORKFLOW_NOTIFY+workflowKey, GlobalConstant.JOB_PREFIX_WORKFLOW_NOTIFY+"GROUP", scheduleDate, null, 0, 0);
            trigger.setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW);
            log.info("******** Trigger created");
            
            log.info(job.getFullName() + " will run at: " + trigger.getNextFireTime() + " & repeat: " + trigger.getRepeatCount() + "/" + trigger.getRepeatInterval());
            sched.scheduleJob(job, trigger);
            log.info("******** Add schedule for wprogressID="+workflowKey+" finished!");
          }
        }
        scheduledJobVec.clear();
      }
    } catch (Exception e) {
      log.error(e, e);
    }
  }
  /**
   * Schedules the general notifications. For each notification in the given Vector, it
   * will store the Vector of objects into a JobDataMap for use in the scheduled jobs.
   *
   * The scheduledJobVec Vector is composed by a list of notications settings, which
   * themselves are again a Vector, for examples,
   * 
   * <pre>
   *   Vector notification = new Vector();
   *   notification.add(workflowStep);
   *   notification.add(workflowProgress);
   *   notification.add(triggerType);
   *   notification.add(scheduleDate);
   *   notification.add(currentProgress);
   *   notification.add(ownerType);
   *   notification.add(stepNotificationRule);
   *   notification.add(targetID);
   *   
   *   allNotifications.add ( notification );
   * </pre>
   *
   *
   * @param scheduledJobVec Vector of the rule-based notifications to be scheduled
   * @throws ApplicationException
   * @see com.dcivision.workflow.core.MailScheduleManager
   * @see com.dcivision.workflow.core.WorkflowProgressManager#createScheduleJobForNotificationRule
   * @see com.dcivision.workflow.bean.MtmWfStepNotificationRule
   * @see com.dcivision.workflow.bean.MtmWfStepNotificationTarget
   */
  public synchronized void scheduleMailByNotifyRule(Vector scheduledJobVec) throws ApplicationException {
    try {
      log.info("scheduledJobVec.size(): " + scheduledJobVec.size());
      Scheduler sched = SchedulerFactory.getScheduler();
      synchronized (sched) {
        HashMap map = new HashMap();
        for (int i = 0; i < scheduledJobVec.size(); i++) {
          WorkflowStep wstep = (WorkflowStep)((Vector)scheduledJobVec.get(i)).get(0);
          java.util.Date  scheduleDate = (java.util.Date)((Vector)scheduledJobVec.get(i)).get(1);
          WorkflowProgress currentProgress = (WorkflowProgress)((Vector)scheduledJobVec.get(i)).get(2);
          MtmWfStepNotificationRule stepNotificationRule = (MtmWfStepNotificationRule)((Vector)scheduledJobVec.get(i)).get(3);
          String workflowKey = null;
          workflowKey = stepNotificationRule.getNotifyTrigger() +"_" +currentProgress.getID().toString() + "_" +stepNotificationRule.getID().toString();
          log.info("******** Key = "+workflowKey);
          JobDataMap dataMap = new org.quartz.JobDataMap();
          dataMap.put("workflowStep", wstep);
          dataMap.put("workflowProgress", currentProgress);
          dataMap.put("stepNotificationRule", stepNotificationRule);
          dataMap.put("actionID",currentProgress.getUpdaterID());
          JobDetail job = new JobDetail(GlobalConstant.JOB_PREFIX_WORKFLOW_NOTIFY+workflowKey, GlobalConstant.JOB_PREFIX_WORKFLOW_NOTIFY+"GROUP", Class.forName(SystemParameterFactory.getSystemParameter(SystemParameterConstant.WORKFLOW_NOTIFICATION_MANAGER)));
          job.setJobDataMap(dataMap);
          
          if (scheduleDate != null) {
            SimpleTrigger trigger = new SimpleTrigger(GlobalConstant.JOB_PREFIX_WORKFLOW_NOTIFY+workflowKey, GlobalConstant.JOB_PREFIX_WORKFLOW_NOTIFY+"GROUP",GlobalConstant.JOB_PREFIX_WORKFLOW_NOTIFY+workflowKey, GlobalConstant.JOB_PREFIX_WORKFLOW_NOTIFY+"GROUP", scheduleDate, null, 0, 0);
            trigger.setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW);
            
            log.info(job.getFullName() + " will run at: " + trigger.getNextFireTime() + " & repeat: " + trigger.getRepeatCount() + "/" + trigger.getRepeatInterval());
            sched.scheduleJob(job, trigger);
            log.info("******** Add schedule for wprogressID="+workflowKey+" finished!");
          }
        }
        scheduledJobVec.clear();
      }
    } catch (Exception e) {
      log.fatal("Error scheduleMailByNotifyRule",e);
    }
  }
  
  
  /**
   * Notify Delegated Person By Mail
   *
   * @param workflowStep The current step
   * @param workflowProgress The current progress
   * @throws ApplicationException
   */
  private void notifyDelegatedPersonByMail(WorkflowStep workflowStep, WorkflowProgress workflowProgress) throws ApplicationException {
    //get the step's actor user record ID list
	//List mtmActorList =(ArrayList) this.gettActorUserList(workflowStep.getID());//simon_del
	ArrayList mtmActorList =(ArrayList) this.gettActorUserList(workflowStep.getID());//simon_add
    ArrayList filterUserIDsList=getfilteredListUserIDsFromWFStep(mtmActorList,workflowStep,workflowProgress);//simon_add
    List delegatedUserIDList = new ArrayList();
    //for (int i=0; i<mtmActorList.size(); i++) {//simon_del
    for (int i=0; i<filterUserIDsList.size(); i++) {//simon_add
      //get the delegated user record ID list by source user ID
      //List tmpList = this.getDelegatedUserAccountBySourceUserID((Integer)mtmActorList.get(i),"E");//simon_del
      List tmpList = this.getDelegatedUserAccountBySourceUserID((Integer)filterUserIDsList.get(i),"E");//simon_add
      if (!Utility.isEmpty(tmpList)) {
        delegatedUserIDList.addAll(tmpList);
      }
    }
    
    //send notify to delegated person by mail
    MailScheduleManager mailScheduleManager = new MailScheduleManager(sessionContainer, dbConn);
    if(!Utility.isEmpty(delegatedUserIDList)){
      mailScheduleManager.sendNotifyDelegatedPersonByMail(workflowStep, workflowProgress, delegatedUserIDList);
    }
  }
  
  /**
   * Notify Delegated Person By System
   * 
   * @param workflowStep
   * @param workflowProgress
   * @throws ApplicationException
   */
  private void notifyDelegatedPersonBySystem(WorkflowStep workflowStep, WorkflowProgress workflowProgress) throws ApplicationException {
    //get the step's actor user record ID list
    //List mtmActorList =this.gettActorUserList(workflowStep.getID());//simon_del
    ArrayList mtmActorList =(ArrayList) this.gettActorUserList(workflowStep.getID());//simon_add
    ArrayList filterUserIDsList=getfilteredListUserIDsFromWFStep(mtmActorList,workflowStep,workflowProgress);//simon_add
    List delegatedUserIDList = new ArrayList();
    //for (int i=0; i<mtmActorList.size(); i++) {//simon_del
    for (int i=0; i<filterUserIDsList.size(); i++) {//simon_add
      //get the delegated user record ID list by source user id
      //List tmpList = this.getDelegatedUserAccountBySourceUserID((Integer)mtmActorList.get(i),"S");//simon_del
      List tmpList = this.getDelegatedUserAccountBySourceUserID((Integer)filterUserIDsList.get(i),"S");//simon_add
      if (!Utility.isEmpty(tmpList)) {
        delegatedUserIDList.addAll(tmpList);
      }
    }
    
    //  send notify to delegated paerson by system
    MailScheduleManager mailScheduleManager = new MailScheduleManager(sessionContainer, dbConn);
    if(!Utility.isEmpty(delegatedUserIDList)){
      mailScheduleManager.sendNotifyDelegatedPersonBySystem(workflowStep, workflowProgress, delegatedUserIDList);
    }
  }
  
  /**
   * Get Delegated User Record ID by Source User ID List
   * 
   * @param sourceUserID
   * @return
   * @throws ApplicationException
   */
  private List getDelegatedUserAccountBySourceUserID(Integer sourceUserID,String notificationType) throws ApplicationException {
    UserDelegationDAObject userDelegationDAO = new UserDelegationDAObject(sessionContainer, dbConn);
    MtmUserRecordUserRoleDAObject mtmUserRoleDAO = new MtmUserRecordUserRoleDAObject(sessionContainer, dbConn);
    MtmUserRecordUserGroupDAObject mtmUserGroupDAO = new MtmUserRecordUserGroupDAObject(sessionContainer, dbConn);
    
    List delegatedUserAccount = userDelegationDAO.getNotificationListBySourceUserID(sourceUserID,Utility.getCurrentTimestamp(),notificationType);
    List resultAccount = new ArrayList();
    List tmpList = null;
    
    if (!Utility.isEmpty(delegatedUserAccount)) {
      for (int i=0; i<delegatedUserAccount.size(); i++) {
        
        UserDelegation tmpUserDelegation = (UserDelegation)delegatedUserAccount.get(i);
        String targetObjectType = tmpUserDelegation.getTargetObjectType();
        
        // delegated to User
        if (GlobalConstant.SUBJECT_TYPE_USER.equals(targetObjectType)) {
          
          Integer tmpSourceID = tmpUserDelegation.getTargetObjectID();
          if(!sessionContainer.getUserRecordID().equals(tmpSourceID)){
            resultAccount.add(tmpSourceID);
          }
          // delegated to Role
        } else if (GlobalConstant.SUBJECT_TYPE_ROLE.equals(targetObjectType)){
          List userRoleIDList = new ArrayList();
          userRoleIDList.add(tmpUserDelegation.getTargetObjectID());
          List userIDList = mtmUserRoleDAO.getUserIDListByUserRoleList(userRoleIDList);
          for (int j=0; j< userIDList.size(); j++) {
            if(!sessionContainer.getUserRecordID().equals(userIDList.get(j))){
              resultAccount.add( userIDList.get(j) );
            }
          }
          // delegated to Group
        } else if (GlobalConstant.SUBJECT_TYPE_GROUP.equals(targetObjectType)) {
          List userGroupIDList = new ArrayList();
          userGroupIDList.add(tmpUserDelegation.getTargetObjectID());
          
          List userRecordIDList = mtmUserGroupDAO.getUserIDListByUserGroupIDList(userGroupIDList);
          for (int k=0; k<userRecordIDList.size(); k++) {
            if(!sessionContainer.getUserRecordID().equals(userRecordIDList.get(k))){
              resultAccount.add( userRecordIDList.get(k) );
            }
          }
        }
      }
    }
    
    return resultAccount;
  }
  
  
  /**
   * Get the Step's Actor User Record ID List
   * 
   * @param workflowStepID
   * @return
   * @throws ApplicationException
   */
  private List gettActorUserList(Integer workflowStepID) throws ApplicationException {
    MtmWorkflowStepUserActorDAObject mtmUserActorDAO = new MtmWorkflowStepUserActorDAObject(sessionContainer, dbConn);
    MtmUserRecordUserRoleDAObject mtmUserRoleDAO = new MtmUserRecordUserRoleDAObject(sessionContainer, dbConn);
    MtmUserRecordUserGroupDAObject mtmUserGroupDAO = new MtmUserRecordUserGroupDAObject(sessionContainer, dbConn);
    
    List mtmUserActorList = mtmUserActorDAO.getListByWorkflowStepID(workflowStepID);
    List userActorResult = new ArrayList();
    
    for (int i=0; i<mtmUserActorList.size(); i++){
      MtmWorkflowStepUserActor mtmUserActor = (MtmWorkflowStepUserActor)mtmUserActorList.get(i);
      String actorType = mtmUserActor.getActorType();
      
      // Actor Type is User
      if (GlobalConstant.SUBJECT_TYPE_USER.equals(actorType)) {
        userActorResult.add(mtmUserActor.getActorID());
        // Actor Type is Role
      } else if (GlobalConstant.SUBJECT_TYPE_ROLE.equals(actorType)) {
        List userRoleIDList = new ArrayList();
        userRoleIDList.add(mtmUserActor.getActorID());
        List userIDList = mtmUserRoleDAO.getUserIDListByUserRoleList(userRoleIDList);
        for (int j=0; j< userIDList.size(); j++) {
          Integer tmpUserID = (Integer)userIDList.get(j);
          if(userActorResult.indexOf(tmpUserID) < 0){
            userActorResult.add(tmpUserID);
          }
        }
        // Actor Type is Group
      } else if (GlobalConstant.SUBJECT_TYPE_GROUP.equals(actorType)) {
        List userGroupIDList = new ArrayList();
        userGroupIDList.add(mtmUserActor.getActorID());
        List userRecordIDList = mtmUserGroupDAO.getUserIDListByUserGroupIDList(userGroupIDList);
        for (int k=0; k<userRecordIDList.size(); k++) {
          Integer tmpUserID = (Integer)userRecordIDList.get(k);
          if(userActorResult.indexOf(tmpUserID) < 0 ){
            userActorResult.add(tmpUserID);
          }
        }
      }
    }
    
    return userActorResult;
  }
  
  /**
   * This function is commented out by Lun at 2005-04-14
   * It seems that it is not used anywhere except DocumentOperationManager.hardDeleteDocument
   * But the method call is commented out too.
   * 
   * @param className The string of class name
   * @param objectID The integer of object ID
   */
  public void hardDeleteObjectByClassNameByID(String className, Integer objectID) throws ApplicationException {
    //get system object list and delete the progress
    MtmWorkflowProgressSystemObjectDAObject systemObjectDAO = new MtmWorkflowProgressSystemObjectDAObject(this.sessionContainer, this.dbConn);
    WorkflowProgressDAObject progressDAO = new WorkflowProgressDAObject(this.sessionContainer, this.dbConn);
    MtmWorkflowProgressUserRecordDAObject progressUserRecordDAO = new MtmWorkflowProgressUserRecordDAObject(this.sessionContainer, this.dbConn);
    WorkflowProgressAttachmentDAObject progressAttachement = new WorkflowProgressAttachmentDAObject(this.sessionContainer, this.dbConn);
    
    List deleteObjectList = systemObjectDAO.getListByBeanClassNameObjectID(className, objectID);
    if (!Utility.isEmpty(deleteObjectList)){
      for (int i=0; i<deleteObjectList.size(); i++){
        MtmWorkflowProgressSystemObject tmpMtmWorkflowProgressSystemObject = (MtmWorkflowProgressSystemObject)deleteObjectList.get(i);
        
        //Get the distinct wrokflow progress ID for MtmWFProgessUserRecord
        List workflowProgressList = progressDAO.getListByTrackID(tmpMtmWorkflowProgressSystemObject.getTrackID());
        String distinctWorkflowProgessIDAry = "";
        String workflowProgressUserRecordIDAry = "";
        //Get workflow progress ID and delete the mtmWorkflowProgressUserRecord
        if (!Utility.isEmpty(workflowProgressList)){
          WorkflowProgress tmpWorkflowProgress = (WorkflowProgress)workflowProgressList.get(i);
          if (Utility.isEmpty(distinctWorkflowProgessIDAry)){
            distinctWorkflowProgessIDAry = tmpWorkflowProgress.getID().toString();
          }
          distinctWorkflowProgessIDAry = distinctWorkflowProgessIDAry + ", " + tmpWorkflowProgress.getID();
          if (!Utility.isEmpty(distinctWorkflowProgessIDAry)) {
            progressUserRecordDAO.deleteByProgressIDAry(distinctWorkflowProgessIDAry);
          }
        }
        //Get workflow Progress User Record ID and delete the workflowProgressAttachement
        if (!Utility.isEmpty(distinctWorkflowProgessIDAry)){
          workflowProgressUserRecordIDAry = progressUserRecordDAO.getDistinctUserRecordIDByWorkflowProgressID(distinctWorkflowProgessIDAry);
          if (!Utility.isEmpty(workflowProgressUserRecordIDAry)) {
            progressAttachement.deleteByMtmWorkflowRpogressUserRecordID(workflowProgressUserRecordIDAry);
          }
        }
        
        progressDAO.deleteByTrackID(tmpMtmWorkflowProgressSystemObject.getTrackID());
        
        systemObjectDAO.deleteByClassNameObjectID(tmpMtmWorkflowProgressSystemObject);
        
        //delete update alert
      }
    }
    
  }
  
  private List getHaveCollocateStepList(Integer beginStepID)throws ApplicationException {
    MtmWorkflowStepWorkflowStepDAObject stepStepDAO = new MtmWorkflowStepWorkflowStepDAObject(this.sessionContainer, this.dbConn);
    WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(this.sessionContainer, this.dbConn);
    List stepStepList = stepStepDAO.getListByParentStepID(beginStepID);
    if (stepStepList.size() <= 1) {
      MtmWorkflowStepWorkflowStep stepStep = (MtmWorkflowStepWorkflowStep)stepStepList.get(0);
      WorkflowStep childStep = (WorkflowStep)stepDAO.getObjectByID(stepStep.getChildStepID());
      if (!WorkflowStep.ACTION_TYPE_END.equals(childStep.getActionType()) && !GlobalConstant.TRUE.equals(stepStep.getLoopBack())) {
        return this.getHaveCollocateStepList(childStep.getID());
      }
    } else {
      for (int i = 0; i < stepStepList.size(); i++) {
        MtmWorkflowStepWorkflowStep stepStep = (MtmWorkflowStepWorkflowStep)stepStepList.get(i);
        if (WorkflowStep.PARENT_TYPE_SINGLE.equals(stepStep.getParentType())) {
          return stepStepList;
        } else {
          WorkflowStep childStep = (WorkflowStep)stepDAO.getObjectByID(stepStep.getChildStepID());
          if (!WorkflowStep.ACTION_TYPE_END.equals(childStep.getActionType()) && !GlobalConstant.TRUE.equals(stepStep.getLoopBack())) {
            return this.getHaveCollocateStepList(childStep.getID());
          }
        }
      }
    }
    return new ArrayList();
  }
  
  /**
   * Adds an End Step after the given step
   *
   * @param step The current Step
   * @param oldStepStep The current step mapping
   * @throws ApplicationException
   * @see com.dcivision.workflow.bean.MtmWorkflowStepWorkflowStep
   */
  private void addEndStep(WorkflowStep step, MtmWorkflowStepWorkflowStep oldStepStep) throws ApplicationException {
    MtmWorkflowStepWorkflowStepDAObject stepStepDAO = new MtmWorkflowStepWorkflowStepDAObject(sessionContainer, dbConn);
    WorkflowActionDAObject actionDAO = new WorkflowActionDAObject(sessionContainer, dbConn);
    WorkflowStepRouteDAObject workflowStepRouteDAO = new WorkflowStepRouteDAObject(sessionContainer, dbConn);
    WorkflowStepFormEquatDAObject formEquatDAO = new WorkflowStepFormEquatDAObject(sessionContainer, dbConn);
    WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(sessionContainer, dbConn);
    Integer nextGroupID = stepStepDAO.getNextGroupIDForWorkflowRecord(step.getWorkflowRecordID());
    if (nextGroupID == null) {
      nextGroupID = new Integer(0);
    }
    int groupCount = nextGroupID.intValue();
    WorkflowStep endStep = (WorkflowStep)stepDAO.getEndStepByWorkflowRecordID(step.getWorkflowRecordID());
    Integer endStepID = endStep.getID();
    if (oldStepStep!=null) {
      MtmWorkflowStepWorkflowStep newStepStep = new MtmWorkflowStepWorkflowStep();
      //Auto task
      if (WorkflowStep.ACTION_TYPE_SYSTEM_AUTO.equals(step.getActionType()) ) {
        WorkflowAction tmpAction = (WorkflowAction) actionDAO.getObjectByID(step.getActionID());
        WorkflowProcessor processor = AutomaticTaskFactory.getInstance(tmpAction);
        int totalRoutine = processor.getNumOfRoutine();
        
        if (totalRoutine>1) {
          for (int j=0; j<totalRoutine; j++) {
            newStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE + j);
            newStepStep.setParentStepID(step.getCurrentStepID());
            newStepStep.setChildStepID(oldStepStep.getChildStepID());
            newStepStep.setGroupID(new Integer(++groupCount));
            newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
            stepStepDAO.insertObject(newStepStep);
          }
        } else {
          newStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE);
          newStepStep.setParentStepID(step.getCurrentStepID());
          newStepStep.setChildStepID(oldStepStep.getChildStepID());
          newStepStep.setGroupID(new Integer(++groupCount));
          newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
          stepStepDAO.insertObject(newStepStep);
        }
      } else if (WorkflowStep.ACTION_TYPE_APPROVAL.equals(step.getActionType())) {
        newStepStep.setParentType(WorkflowStep.PARENT_TYPE_APPROVED);
        newStepStep.setParentStepID(step.getCurrentStepID());
        newStepStep.setGroupID(new Integer(++groupCount));
        newStepStep.setChildStepID(oldStepStep.getChildStepID());
        newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
        stepStepDAO.insertObject(newStepStep);
        
        newStepStep = new MtmWorkflowStepWorkflowStep();
        newStepStep.setParentType(WorkflowStep.PARENT_TYPE_REJECTED);
        newStepStep.setParentStepID(step.getCurrentStepID());
        newStepStep.setGroupID(new Integer(++groupCount));
        newStepStep.setChildStepID(endStepID);
        newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_YES);
        stepStepDAO.insertObject(newStepStep);
        //Yes / No Task
      } else if (WorkflowStep.ACTION_TYPE_BOOLEAN.equals(step.getActionType())) {
        newStepStep.setParentType(WorkflowStep.PARENT_TYPE_YES);
        newStepStep.setParentStepID(step.getCurrentStepID());
        newStepStep.setGroupID(new Integer(++groupCount));
        newStepStep.setChildStepID(oldStepStep.getChildStepID());
        newStepStep.setLoopBack(oldStepStep.getLoopBack());
        
        stepStepDAO.insertObject(newStepStep);
        
        newStepStep = new MtmWorkflowStepWorkflowStep();
        newStepStep.setParentType(WorkflowStep.PARENT_TYPE_NO);
        newStepStep.setParentStepID(step.getCurrentStepID());
        newStepStep.setGroupID(new Integer(++groupCount));
        newStepStep.setChildStepID(endStepID);
        newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_YES);
        
        stepStepDAO.insertObject(newStepStep);
        //Form Value Driven Task
      } else if (WorkflowStep.ACTION_TYPE_FORM_DRIVEN.equals(step.getActionType())) {
        List formEquationList = formEquatDAO.getListByWorkflowStepID(step.getID());
        for(int k=0; k<formEquationList.size(); k++){
          newStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE+k);
          newStepStep.setParentStepID(step.getCurrentStepID());
          newStepStep.setGroupID(new Integer(++groupCount));
          
          newStepStep.setChildStepID(oldStepStep.getChildStepID());
          newStepStep.setLoopBack(oldStepStep.getLoopBack());
          //          newStepStep.setChildStepID(oldStepStep.getChildStepID());
          //          newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
          stepStepDAO.insertObject(newStepStep);
        }
        
        //Decision Point Task
      } else if (WorkflowStep.ACTION_TYPE_USER_DEFINED_TASK.equals(step.getActionType())) {
        List workflowStepRouteList = workflowStepRouteDAO.getListByWorkflowStepID(step.getCurrentStepID());
        for(int k=0; k<workflowStepRouteList.size(); k++){
          newStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE+k);
          newStepStep.setParentStepID(step.getCurrentStepID());
          newStepStep.setGroupID(new Integer(++groupCount));
          if(k == 0){
            newStepStep.setChildStepID(oldStepStep.getChildStepID());
            newStepStep.setLoopBack(oldStepStep.getLoopBack());
          } else {
            newStepStep.setChildStepID(endStepID);
            newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_YES);
          }
          stepStepDAO.insertObject(newStepStep);
        }
      } else {
        //      if (!WorkflowStep.ACTION_TYPE_APPROVAL.equals(step.getActionType()) &&
        //          !WorkflowStep.ACTION_TYPE_BOOLEAN.equals(step.getActionType()) &&
        //          !WorkflowStep.ACTION_TYPE_SYSTEM_AUTO.equals(step.getActionType()) &&
        //          !WorkflowStep.ACTION_TYPE_FORM_DRIVEN.equals(step.getActionType()) &&
        //          !WorkflowStep.ACTION_TYPE_USER_DEFINED_TASK.equals(step.getActionType()) ) {
        newStepStep.setParentType(WorkflowStep.PARENT_TYPE_SINGLE);
        newStepStep.setParentStepID(step.getCurrentStepID());
        newStepStep.setChildStepID(oldStepStep.getChildStepID());
        newStepStep.setGroupID(new Integer(++groupCount));
        newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
        stepStepDAO.insertObject(newStepStep);
      }
    }
  }
  
  /**
   * Deletes the notifyTarget and notifyRule Record for workflow step
   * @param workflowStepID The workflow step ID
   * @throws ApplicationException
   */
  private void deleteNotifyRuleAndNotifyTargetByWorkflowStepID(Integer workflowStepID) throws ApplicationException {
    MtmWfStepNotificationRuleDAObject notifyRuleDAO = new MtmWfStepNotificationRuleDAObject(sessionContainer, dbConn);
    MtmWfStepNotificationTargetDAObject notifyTargetDAO = new MtmWfStepNotificationTargetDAObject(sessionContainer, dbConn);
    
    List notifyRuleList = notifyRuleDAO.getListByWorkflowStepID(workflowStepID);
    notifyRuleDAO.deleteNotifyRuleForSpecialActorByWorkflowStepID(workflowStepID);
    log.debug("The current Step is notify Rule size is :::::::::::" + notifyRuleList.size());
    for (int i = 0; !Utility.isEmpty(notifyRuleList) && i < notifyRuleList.size(); i++) {
      MtmWfStepNotificationRule notifyRule = (MtmWfStepNotificationRule)notifyRuleList.get(i);
      notifyTargetDAO.deleteNotificationTargetByRuleID(notifyRule.getID());
    }
    if(!Utility.isEmpty(notifyRuleList)){
      notifyRuleDAO.deleteNotifyRuleByWorkflowStepID(workflowStepID); 
    }
    
  }
  
  private void createLoopbackStep(MtmWorkflowStepWorkflowStep stepStep, Integer objectStepID)throws ApplicationException {
    WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(this.sessionContainer, this.dbConn);
    MtmWorkflowStepWorkflowStepDAObject stepStepDAO = new MtmWorkflowStepWorkflowStepDAObject(sessionContainer, dbConn);
    WorkflowStep workflowStep = (WorkflowStep) stepDAO.getObjectByID(stepStep.getChildStepID());
    if (WorkflowStep.END_STEP_SEQ_NO.equals(workflowStep.getStepSeq())) {
      Integer nextGroupID = stepStepDAO.getNextGroupIDForWorkflowRecord(workflowStep.getWorkflowRecordID());
      stepStep.setChildStepID(objectStepID);
      stepStep.setGroupID(nextGroupID);
      stepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_YES);
      stepStep.setLoopBackAllBranch(GlobalConstant.TRUE);
      stepStep = (MtmWorkflowStepWorkflowStep) stepStepDAO.updateObject(stepStep);
    } else {
      List childStepList = stepStepDAO.getListByParentStepID(stepStep.getChildStepID());
      for (int j = 0; j < childStepList.size(); j++) {
        MtmWorkflowStepWorkflowStep curStepStep = (MtmWorkflowStepWorkflowStep)childStepList.get(j);
        WorkflowStep curStep = (WorkflowStep) stepDAO.getObjectByID(curStepStep.getChildStepID());
        if (!(GlobalConstant.TRUE.equals(curStepStep.getLoopBack()) && !WorkflowStep.END_STEP_SEQ_NO.equals(curStep.getStepSeq()))) {
          this.createLoopbackStep(curStepStep,objectStepID);
        }
      }
    }
  }
  
  /**
   * handle merge step
   * @param workflowStep
   * @throws ApplicationException
   */
  private void handelMergeStep(WorkflowStep workflowStep) throws ApplicationException{
    WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(sessionContainer, dbConn);
    MtmWorkflowStepWorkflowStepDAObject stepStepDAO = new MtmWorkflowStepWorkflowStepDAObject(sessionContainer, dbConn);
    String oldParentStepIDs = "";
    String newParentStepIDs = "";   
    
    
    //* find group id
    log.debug("add workflow step workflow step record ");
    Integer nextGroupID = stepStepDAO.getNextGroupIDForWorkflowRecord(workflowStep.getWorkflowRecordID());
    if (nextGroupID == null) {
      nextGroupID = new Integer(0);
    }
    int groupCount = nextGroupID.intValue();
    int mergeGroupCount = groupCount;
    
    //get the endStepID
    WorkflowStep endStep = (WorkflowStep)stepDAO.getEndStepByWorkflowRecordID(workflowStep.getWorkflowRecordID());
    
    //get the old parente step ID string.
    //there should the parent step given out ready and the other parent step with the same group id BY TC FOR EIP-1548
    MtmWorkflowStepWorkflowStep oldStepStep = (MtmWorkflowStepWorkflowStep)stepStepDAO.getParentStepToChildStepRecord(workflowStep.getWorkflowRecordID(), workflowStep.getParentStepID(), workflowStep.getParentType(), workflowStep.getID());
    List oldParentStepList = stepStepDAO.getListByChildStepIDAndLoopBackAndGroupID(workflowStep.getID(), MtmWorkflowStepWorkflowStep.LOOP_BACK_NO,oldStepStep.getGroupID());
    int[] oldParentStepIDArray = new int[oldParentStepList.size()];
    
    for (int i=0; i<oldParentStepList.size(); i++) {
      MtmWorkflowStepWorkflowStep stepStep = (MtmWorkflowStepWorkflowStep)oldParentStepList.get(i);
      oldParentStepIDs += stepStep.getParentType() + "-" + stepStep.getParentStepID() + "|";
      oldParentStepIDArray[i]=stepStep.getParentStepID().intValue();
    }  
    
    //check if the MergeStep not change then stop it TC FOR BUG-1548
    if(oldParentStepsComparedNewParentSteps(oldParentStepIDArray,workflowStep.getParentStepIDs())){
      return;
    }
    //Insert the new Step to Step Relation 
    for (int i=0; i<workflowStep.getParentStepIDs().length; i++) {
      String parentStepIDs = workflowStep.getParentStepIDs()[i];
      String parentType = parentStepIDs.substring(0, parentStepIDs.indexOf("-"));
      String parentStepID = parentStepIDs.substring(parentStepIDs.indexOf("-") + 1);
      
      newParentStepIDs += parentStepIDs + "|";      
      MtmWorkflowStepWorkflowStep newStepStep = null;
      log.debug("parentStepDIs:"+parentStepIDs);
      
      //Insert the add new step relation 
      if(oldParentStepIDs.indexOf(parentStepIDs+"|") < 0){
        List stepStepList = stepStepDAO.getListByParentStepIDAndParentType(new Integer(parentStepID), parentType);
        //delete all child realtion ship of the parentStep
        for (int j=0; j<stepStepList.size(); j++) {
          MtmWorkflowStepWorkflowStep tempStep2 = (MtmWorkflowStepWorkflowStep)stepStepList.get(j);
          stepStepDAO.deleteObject(tempStep2);
        }
        //create new parent-child relationShip
        newStepStep = new MtmWorkflowStepWorkflowStep();
        newStepStep.setParentType(parentType);
        newStepStep.setParentStepID(new Integer(parentStepID));
        newStepStep.setChildStepID(workflowStep.getID());
        newStepStep.setGroupID(new Integer(mergeGroupCount));
        newStepStep.setLoopBack(MtmWorkflowStepWorkflowStep.LOOP_BACK_NO);
        stepStepDAO.insertObject(newStepStep);
        
        //reset the group ID 
      } else {
        log.debug("==============================="+workflowStep.getWorkflowRecordID()+":"+ new Integer(parentStepID)+":"+ parentType+":"+workflowStep.getID() );
        newStepStep = (MtmWorkflowStepWorkflowStep)stepStepDAO.getParentStepToChildStepRecord(workflowStep.getWorkflowRecordID(), new Integer(parentStepID), parentType, workflowStep.getID());
        newStepStep.setGroupID(new Integer(mergeGroupCount));
        stepStepDAO.updateObject(newStepStep);
      }
    }
    log.debug("newParentStepIDs:"+newParentStepIDs);
    //if the old parent step ID not in the new parent step ID, set the childen step ID to end step ID
    for (int i=0; i<oldParentStepList.size(); i++) {
      MtmWorkflowStepWorkflowStep stepStep = (MtmWorkflowStepWorkflowStep)oldParentStepList.get(i);
      String tempParentStepIDs = stepStep.getParentType() + "-" + stepStep.getParentStepID() + "|";
      if(newParentStepIDs.indexOf(tempParentStepIDs) < 0 ){
        List tempParentStepStepList = stepStepDAO.getListByParentStepIDAndParentType(stepStep.getParentStepID(), stepStep.getParentType());
        if(!Utility.isEmpty(tempParentStepStepList)&&tempParentStepStepList.size()>1){
          //if have parall step , delete this step
          stepStepDAO.deleteObject(stepStep);
        }else{
          stepStep.setChildStepID(endStep.getID());
          stepStep.setGroupID(new Integer(++groupCount));
          stepStepDAO.updateObject(stepStep);
        }
      }    
    }  
  }
  //simon_add_S
  
  private boolean oldParentStepsComparedNewParentSteps(int[] oldParentStepIDs,String[] newParentStepStrs){
    int[] newIntParentIDs = new int[newParentStepStrs.length];
    for(int i=0;i<newParentStepStrs.length;i++){
      String parentStepID = newParentStepStrs[i].substring(newParentStepStrs[i].indexOf("-") + 1);
      newIntParentIDs[i] = Integer.valueOf(parentStepID).intValue();
    }
    Arrays.sort(oldParentStepIDs);
    Arrays.sort(newIntParentIDs);
    return Arrays.equals(oldParentStepIDs, newIntParentIDs);
 
  }
  /**
   * get the filtered UserIDs List From the WorkFlow Step Filter
   * @param userIdsList
   * @param wstep
   * @param wprogress
   * @return
   * @throws ApplicationException
   */
  private ArrayList getfilteredListUserIDsFromWFStep(ArrayList userIdsList, WorkflowStep wstep, WorkflowProgress wprogress) throws ApplicationException {
	WorkflowStepDAObject stepDAO = new WorkflowStepDAObject(sessionContainer, dbConn);
    return stepDAO.getfilteredListUserIDsFromWFStep(userIdsList, wstep, wprogress);
  }
  //simon_add_E
}